X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=yardstick%2Fcommon%2Fansible_common.py;h=dee7044a5e9a9043eb768a1cfd23c7496fbb471b;hb=bdf55c2a6f000e0ca6462a9df4a0d411c3dd029e;hp=0cafa97087c062900cc34c67196e06de265586bd;hpb=b39fe5457e846e734e980666bc209fb7c07a6bdc;p=yardstick.git diff --git a/yardstick/common/ansible_common.py b/yardstick/common/ansible_common.py index 0cafa9708..dee7044a5 100644 --- a/yardstick/common/ansible_common.py +++ b/yardstick/common/ansible_common.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import - import cgitb import collections import contextlib as cl @@ -23,17 +21,18 @@ import os from collections import Mapping, MutableMapping, Iterable, Callable, deque from functools import partial from itertools import chain -from subprocess import CalledProcessError, Popen, PIPE -from tempfile import NamedTemporaryFile +import subprocess +import tempfile import six -import six.moves.configparser as ConfigParser +from six.moves import configparser import yaml from six import StringIO from chainmap import ChainMap +from oslo_serialization import jsonutils from yardstick.common.utils import Timer - +from yardstick.common import constants as consts cgitb.enable(format="text") @@ -133,10 +132,9 @@ class CustomTemporaryFile(object): else: self.data_types = self.DEFAULT_DATA_TYPES # must open "w+" so unicode is encoded correctly - self.creator = partial(NamedTemporaryFile, mode="w+", delete=False, - dir=directory, - prefix=prefix, - suffix=self.suffix) + self.creator = partial( + tempfile.NamedTemporaryFile, mode="w+", delete=False, + dir=directory, prefix=prefix, suffix=self.suffix) def make_context(self, data, write_func, descriptor='data'): return TempfileContext(data, write_func, descriptor, self.data_types, @@ -190,8 +188,8 @@ class FileNameGenerator(object): if not prefix.endswith('_'): prefix += '_' - temp_file = NamedTemporaryFile(delete=False, dir=directory, - prefix=prefix, suffix=suffix) + temp_file = tempfile.NamedTemporaryFile(delete=False, dir=directory, + prefix=prefix, suffix=suffix) with cl.closing(temp_file): return temp_file.name @@ -298,8 +296,9 @@ class AnsibleNode(MutableMapping): def gen_inventory_line(self): inventory_params = self.get_inventory_params() # use format to convert ints + # sort to ensure consistent key value ordering formatted_args = (u"{}={}".format(*entry) for entry in - inventory_params.items()) + sorted(inventory_params.items())) line = u" ".join(chain([self['name']], formatted_args)) return line @@ -434,6 +433,7 @@ class AnsibleCommon(object): ansible_dict = dict(os.environ, **{ "ANSIBLE_LOG_PATH": os.path.join(directory, log_file), "ANSIBLE_LOG_BASE": directory, + "ANSIBLE_ROLES_PATH": consts.ANSIBLE_ROLES_PATH, # # required for SSH to work # "ANSIBLE_SSH_ARGS": "-o UserKnownHostsFile=/dev/null " # "-o GSSAPIAuthentication=no " @@ -471,7 +471,9 @@ class AnsibleCommon(object): prefix = '_'.join([self.prefix, prefix, 'inventory']) ini_temp_file = IniMapTemporaryFile(directory=directory, prefix=prefix) - inventory_config = ConfigParser.ConfigParser(allow_no_value=True) + inventory_config = configparser.ConfigParser(allow_no_value=True) + # disable default lowercasing + inventory_config.optionxform = str return ini_temp_file.make_context(self.inventory_dict, write_func, descriptor='inventory') @@ -504,6 +506,58 @@ class AnsibleCommon(object): timeout = 1200.0 return timeout + def _generate_ansible_cfg(self, directory): + parser = configparser.ConfigParser() + parser.add_section('defaults') + parser.set('defaults', 'host_key_checking', 'False') + + cfg_path = os.path.join(directory, 'ansible.cfg') + with open(cfg_path, 'w') as f: + parser.write(f) + + def get_sut_info(self, directory, sut_dir='sut'): + if not os.path.isdir(directory): + raise OSError('No such directory: %s' % directory) + + self._generate_ansible_cfg(directory) + + prefix = 'tmp' + self.gen_inventory_ini_dict() + ini_file = self._gen_ansible_inventory_file(directory, prefix=prefix) + with ini_file as f: + inventory_path = str(f) + + self._exec_get_sut_info_cmd(directory, inventory_path, sut_dir) + + sut_dir = os.path.join(directory, sut_dir) + sut_info = self._gen_sut_info_dict(sut_dir) + + return sut_info + + def _exec_get_sut_info_cmd(self, directory, inventory_path, sut_dir): + cmd = ['ansible', 'all', '-m', 'setup', '-i', + inventory_path, '--tree', sut_dir] + + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd=directory) + output, _ = proc.communicate() + retcode = proc.wait() + LOG.debug("exit status = %s", retcode) + if retcode != 0: + raise subprocess.CalledProcessError(retcode, cmd, output) + + def _gen_sut_info_dict(self, sut_dir): + sut_info = {} + + if os.path.isdir(sut_dir): + root, _, files = next(os.walk(sut_dir)) + for filename in files: + abs_path = os.path.join(root, filename) + with open(abs_path) as f: + data = jsonutils.load(f) + sut_info[filename] = data + + return sut_info + def execute_ansible(self, playbooks, directory, timeout=None, extra_vars=None, ansible_check=False, prefix='tmp', verbose=False): @@ -513,7 +567,7 @@ class AnsibleCommon(object): # playbook dir: use include to point to files in consts.ANSIBLE_DIR if not os.path.isdir(directory): - raise OSError("Not a directory, %s", directory) + raise OSError("Not a directory, %s" % directory) timeout = self.get_timeout(timeout, self.default_timeout) self.counter += 1 @@ -560,12 +614,13 @@ class AnsibleCommon(object): # 'timeout': timeout / 2, }) with Timer() as timer: - proc = Popen(cmd, stdout=PIPE, **exec_args) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, + **exec_args) output, _ = proc.communicate() retcode = proc.wait() LOG.debug("exit status = %s", retcode) if retcode != 0: - raise CalledProcessError(retcode, cmd, output) + raise subprocess.CalledProcessError(retcode, cmd, output) timeout -= timer.total_seconds() cmd.remove("--syntax-check") @@ -575,10 +630,10 @@ class AnsibleCommon(object): # TODO: add timeout support of use subprocess32 backport # 'timeout': timeout, }) - proc = Popen(cmd, stdout=PIPE, **exec_args) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, **exec_args) output, _ = proc.communicate() retcode = proc.wait() LOG.debug("exit status = %s", retcode) if retcode != 0: - raise CalledProcessError(retcode, cmd, output) + raise subprocess.CalledProcessError(retcode, cmd, output) return output