Move "read_yaml_file" to common.yaml_loader
[yardstick.git] / yardstick / benchmark / contexts / base.py
1 ##############################################################################
2 # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import abc
11 import errno
12 import six
13 import os
14
15 from yardstick.common import constants
16 from yardstick.common import utils
17 from yardstick.common import yaml_loader
18 from yardstick.common.constants import YARDSTICK_ROOT_PATH
19
20
21 class Flags(object):
22     """Class to represent the status of the flags in a context"""
23
24     _FLAGS = {'no_setup': False,
25               'no_teardown': False,
26               'os_cloud_config': constants.OS_CLOUD_DEFAULT_CONFIG}
27
28     def __init__(self, **kwargs):
29         for name, value in self._FLAGS.items():
30             setattr(self, name, value)
31
32         for name, value in ((name, value) for (name, value) in kwargs.items()
33                             if name in self._FLAGS):
34             setattr(self, name, value)
35
36     def parse(self, **kwargs):
37         """Read in values matching the flags stored in this object"""
38         if not kwargs:
39             return
40
41         for name, value in ((name, value) for (name, value) in kwargs.items()
42                             if name in self._FLAGS):
43             setattr(self, name, value)
44
45
46 @six.add_metaclass(abc.ABCMeta)
47 class Context(object):
48     """Class that represents a context in the logical model"""
49     list = []
50     SHORT_TASK_ID_LEN = 8
51
52     def __init__(self, host_name_separator='.'):
53         Context.list.append(self)
54         self._flags = Flags()
55         self._name = None
56         self._task_id = None
57         self.file_path = None
58         self._host_name_separator = host_name_separator
59
60     def init(self, attrs):
61         """Initiate context"""
62         self._name = attrs['name']
63         self._task_id = attrs['task_id']
64         self._flags.parse(**attrs.get('flags', {}))
65         self._name_task_id = '{}-{}'.format(
66             self._name, self._task_id[:self.SHORT_TASK_ID_LEN])
67
68     def split_host_name(self, name):
69         if (isinstance(name, six.string_types)
70                 and self._host_name_separator in name):
71             return tuple(name.split(self._host_name_separator, 1))
72         return None, None
73
74     def read_pod_file(self, attrs):
75         self.file_path = file_path = attrs.get("file", "pod.yaml")
76         try:
77             cfg = yaml_loader.read_yaml_file(self.file_path)
78         except IOError as io_error:
79             if io_error.errno != errno.ENOENT:
80                 raise
81
82             self.file_path = os.path.join(YARDSTICK_ROOT_PATH, file_path)
83             cfg = yaml_loader.read_yaml_file(self.file_path)
84
85         self.nodes.extend(cfg["nodes"])
86         self.controllers.extend([node for node in cfg["nodes"]
87                                  if node.get("role") == "Controller"])
88         self.computes.extend([node for node in cfg["nodes"]
89                               if node.get("role") == "Compute"])
90         self.baremetals.extend([node for node in cfg["nodes"]
91                                 if node.get("role") == "Baremetal"])
92         return cfg
93
94     @property
95     def name(self):
96         if self._flags.no_setup or self._flags.no_teardown:
97             return self._name
98         else:
99             return self._name_task_id
100
101     @property
102     def assigned_name(self):
103         return self._name
104
105     @property
106     def host_name_separator(self):
107         return self._host_name_separator
108
109     @staticmethod
110     def get_cls(context_type):
111         """Return class of specified type."""
112         for context in utils.itersubclasses(Context):
113             if context_type == context.__context_type__:
114                 return context
115         raise RuntimeError("No such context_type %s" % context_type)
116
117     @staticmethod
118     def get(context_type):
119         """Returns instance of a context for context type.
120         """
121         return Context.get_cls(context_type)()
122
123     @abc.abstractmethod
124     def deploy(self):
125         """Deploy context."""
126
127     @abc.abstractmethod
128     def undeploy(self):
129         """Undeploy context."""
130         self._delete_context()
131
132     def _delete_context(self):
133         Context.list.remove(self)
134
135     @abc.abstractmethod
136     def _get_server(self, attr_name):
137         """get server info by name from context
138         """
139
140     @abc.abstractmethod
141     def _get_network(self, attr_name):
142         """get network info by name from context
143         """
144
145     @staticmethod
146     def get_server(attr_name):
147         """lookup server info by name from context
148         attr_name: either a name for a server created by yardstick or a dict
149         with attribute name mapping when using external heat templates
150         """
151         servers = (context._get_server(attr_name) for context in Context.list)
152         try:
153             return next(s for s in servers if s)
154         except StopIteration:
155             raise ValueError("context not found for server %r" %
156                              attr_name)
157
158     @staticmethod
159     def get_physical_nodes():
160         """return physical node names for all contexts"""
161         physical_nodes = {}
162         for context in Context.list:
163             nodes = context._get_physical_nodes()
164             physical_nodes.update({context._name: nodes})
165
166         return physical_nodes
167
168     @staticmethod
169     def get_physical_node_from_server(server_name):
170         """return physical nodes for all contexts"""
171         context = Context.get_context_from_server(server_name)
172         if context == None:
173             return  None
174
175         return context._get_physical_node_for_server(server_name)
176
177     @staticmethod
178     def get_context_from_server(attr_name):
179         """lookup context info by name from node config
180         attr_name: either a name of the node created by yardstick or a dict
181         with attribute name mapping when using external templates
182
183         :returns Context instance
184         """
185         servers = ((context._get_server(attr_name), context)
186                    for context in Context.list)
187         try:
188             return next(con for s, con in servers if s)
189         except StopIteration:
190             raise ValueError("context not found for name %r" %
191                              attr_name)
192
193     @staticmethod
194     def get_network(attr_name):
195         """lookup server info by name from context
196         attr_name: either a name for a server created by yardstick or a dict
197         with attribute name mapping when using external heat templates
198         """
199
200         networks = (context._get_network(attr_name) for context in Context.list)
201         try:
202             return next(n for n in networks if n)
203         except StopIteration:
204             raise ValueError("context not found for server %r" %
205                              attr_name)
206
207     @abc.abstractmethod
208     def _get_physical_nodes(self):
209         """return the list of physical nodes in context"""
210
211     @abc.abstractmethod
212     def _get_physical_node_for_server(self, server_name):
213         """ Find physical node for given server
214
215         :param server_name: (string) Server name in scenario
216         :return string:  <node_name>.<context_name>
217         """