1 ##############################################################################
2 # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others.
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 ##############################################################################
19 from yardstick import ssh
20 from yardstick.benchmark import contexts
21 from yardstick.benchmark.contexts.base import Context
22 from yardstick.common.constants import ANSIBLE_DIR, YARDSTICK_ROOT_PATH
23 from yardstick.common.ansible_common import AnsibleCommon
24 from yardstick.common.exceptions import ContextUpdateCollectdForNodeError
26 LOG = logging.getLogger(__name__)
28 DEFAULT_DISPATCH = 'script'
31 class NodeContext(Context):
32 """Class that handle nodes info"""
34 __context_type__ = contexts.CONTEXT_NODE
45 self.DISPATCH_TYPES = {
46 "ansible": self._dispatch_ansible,
47 "script": self._dispatch_script,
49 super(NodeContext, self).__init__()
51 def init(self, attrs):
52 """initializes itself from the supplied arguments"""
53 super(NodeContext, self).init(attrs)
55 cfg = self.read_pod_file(attrs)
57 self.env = attrs.get('env', {})
59 LOG.debug("Env: %r", self.env)
61 # add optional static network definition
62 self.networks.update(cfg.get("networks", {}))
65 config_type = self.env.get('type', DEFAULT_DISPATCH)
66 self.DISPATCH_TYPES[config_type]("setup")
69 config_type = self.env.get('type', DEFAULT_DISPATCH)
70 self.DISPATCH_TYPES[config_type]("teardown")
71 super(NodeContext, self).undeploy()
73 def _dispatch_script(self, key):
74 steps = self.env.get(key, [])
76 for host, info in step.items():
77 self._execute_script(host, info)
79 def _dispatch_ansible(self, key):
81 playbooks = self.env[key]
85 self._do_ansible_job(playbooks)
87 def _do_ansible_job(self, playbooks):
88 self.ansible_exec = AnsibleCommon(nodes=self.nodes,
90 # playbooks relative to ansible dir
91 # playbooks can also be a list of playbooks
92 self.ansible_exec.gen_inventory_ini_dict()
93 if isinstance(playbooks, six.string_types):
94 playbooks = [playbooks]
95 playbooks = [self.fix_ansible_path(playbook) for playbook in playbooks]
97 tmpdir = tempfile.mkdtemp(prefix='ansible-')
98 self.ansible_exec.execute_ansible(playbooks, tmpdir,
99 verbose=self.env.get("verbose",
102 def fix_ansible_path(self, playbook):
103 if not os.path.isabs(playbook):
104 # make relative paths absolute in ANSIBLE_DIR
105 playbook = os.path.join(ANSIBLE_DIR, playbook)
108 def _get_physical_nodes(self):
111 def _get_physical_node_for_server(self, server_name):
113 node_name, context_name = self.split_host_name(server_name)
115 if context_name is None or self.name != context_name:
118 for n in (n for n in self.nodes if n["name"] == node_name):
119 return "{}.{}".format(n["name"], self._name)
123 def update_collectd_options_for_node(self, options, attr_name):
124 node_name, _ = self.split_host_name(attr_name)
126 matching_nodes = (n for n in self.nodes if n["name"] == node_name)
128 node = next(matching_nodes)
129 except StopIteration:
130 raise ContextUpdateCollectdForNodeError(attr_name=attr_name)
132 node["collectd"] = options
134 def _get_server(self, attr_name):
135 """lookup server info by name from context
136 attr_name: a name for a server listed in nodes config file
138 node_name, name = self.split_host_name(attr_name)
139 if name is None or self.name != name:
142 matching_nodes = (n for n in self.nodes if n["name"] == node_name)
145 # A clone is created in order to avoid affecting the
147 node = dict(next(matching_nodes))
148 except StopIteration:
152 duplicate = next(matching_nodes)
153 except StopIteration:
156 raise ValueError("Duplicate nodes!!! Nodes: %s %s" %
159 node["name"] = attr_name
160 node.setdefault("interfaces", {})
163 def _get_network(self, attr_name):
164 if not isinstance(attr_name, collections.Mapping):
165 network = self.networks.get(attr_name)
168 # Don't generalize too much Just support vld_id
169 vld_id = attr_name.get('vld_id', {})
170 # for node context networks are dicts
171 iter1 = (n for n in self.networks.values() if n.get('vld_id') == vld_id)
172 network = next(iter1, None)
179 "name": network["name"],
180 "vld_id": network.get("vld_id"),
181 "segmentation_id": network.get("segmentation_id"),
182 "network_type": network.get("network_type"),
183 "physical_network": network.get("physical_network"),
187 def _execute_script(self, node_name, info):
188 if node_name == 'local':
189 self._execute_local_script(info)
191 self._execute_remote_script(node_name, info)
193 def _execute_remote_script(self, node_name, info):
194 prefix = self.env.get('prefix', '')
195 script, options = self._get_script(info)
197 script_file = pkg_resources.resource_filename(prefix, script)
199 self._get_client(node_name)
200 self.client._put_file_shell(script_file, '~/{}'.format(script))
202 cmd = 'sudo bash {} {}'.format(script, options)
203 status, _, stderr = self.client.execute(cmd)
205 raise RuntimeError(stderr)
207 def _execute_local_script(self, info):
208 script, options = self._get_script(info)
209 script = os.path.join(YARDSTICK_ROOT_PATH, script)
210 cmd = ['bash', script, options]
212 p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
213 LOG.debug('\n%s', p.communicate()[0])
215 def _get_script(self, info):
216 return info.get('script'), info.get('options', '')
218 def _get_client(self, node_name):
219 node = self._get_node_info(node_name.strip())
222 raise SystemExit('No such node')
224 self.client = ssh.SSH.from_node(node, defaults={'user': 'ubuntu'})
226 self.client.wait(timeout=600)
228 def _get_node_info(self, name):
229 return next((n for n in self.nodes if n['name'].strip() == name))