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