From c3f91fac56cd938c408c62ff44f5ff039b1fd8e6 Mon Sep 17 00:00:00 2001 From: chenjiankun Date: Wed, 14 Mar 2018 09:41:01 +0000 Subject: Create API to get SUT information JIRA: YARDSTICK-1072 We need to show SUT information in GUI. Change-Id: I40bcd513f3c6a443c82764687637ac5258b99584 Signed-off-by: chenjiankun --- api/resources/v2/environments.py | 35 +++++++- api/urls.py | 1 + yardstick/common/exceptions.py | 8 ++ yardstick/service/__init__.py | 12 +++ yardstick/service/environment.py | 101 +++++++++++++++++++++++ yardstick/tests/unit/service/__init__.py | 0 yardstick/tests/unit/service/test_environment.py | 49 +++++++++++ 7 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 yardstick/service/__init__.py create mode 100644 yardstick/service/environment.py create mode 100644 yardstick/tests/unit/service/__init__.py create mode 100644 yardstick/tests/unit/service/test_environment.py diff --git a/api/resources/v2/environments.py b/api/resources/v2/environments.py index 158e98be7..7e587be85 100644 --- a/api/resources/v2/environments.py +++ b/api/resources/v2/environments.py @@ -11,6 +11,7 @@ import logging from oslo_serialization import jsonutils from docker import Client +from docker.errors import APIError from api import ApiResource from api.database.v2.handlers import V2EnvironmentHandler @@ -20,6 +21,7 @@ from api.database.v2.handlers import V2ContainerHandler from yardstick.common.utils import result_handler from yardstick.common.utils import change_obj_to_dict from yardstick.common import constants as consts +from yardstick.service.environment import Environment LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) @@ -124,10 +126,41 @@ class V2Environment(ApiResource): LOG.debug('container name: %s', container.name) try: client.remove_container(container.name, force=True) - except Exception: + except APIError: LOG.exception('remove container failed') container_handler.delete_by_uuid(v) environment_handler.delete_by_uuid(environment_id) return result_handler(consts.API_SUCCESS, {'environment': environment_id}) + + +class V2SUT(ApiResource): + + def get(self, environment_id): + try: + uuid.UUID(environment_id) + except ValueError: + return result_handler(consts.API_ERROR, 'invalid environment id') + + environment_handler = V2EnvironmentHandler() + try: + environment = environment_handler.get_by_uuid(environment_id) + except ValueError: + return result_handler(consts.API_ERROR, 'no such environment id') + + if not environment.pod_id: + return result_handler(consts.API_SUCCESS, {'sut': {}}) + + pod_handler = V2PodHandler() + try: + pod = pod_handler.get_by_uuid(environment.pod_id) + except ValueError: + return result_handler(consts.API_ERROR, 'no such pod id') + else: + pod_content = pod.content + + env = Environment(pod=pod_content) + sut_info = env.get_sut_info() + + return result_handler(consts.API_SUCCESS, {'sut': sut_info}) diff --git a/api/urls.py b/api/urls.py index 4b8e39e8f..9f0abcade 100644 --- a/api/urls.py +++ b/api/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ Url('/api/v2/yardstick/environments', 'v2_environments'), Url('/api/v2/yardstick/environments/action', 'v2_environments'), Url('/api/v2/yardstick/environments/', 'v2_environment'), + Url('/api/v2/yardstick/environments//sut', 'v2_sut'), Url('/api/v2/yardstick/openrcs', 'v2_openrcs'), Url('/api/v2/yardstick/openrcs/action', 'v2_openrcs'), diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index a0edd3ba9..633b36f91 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -112,3 +112,11 @@ class ScenarioCreateSubnetError(YardstickException): class ScenarioDeleteRouterError(YardstickException): message = 'Delete Neutron Router Scenario failed' + + +class MissingPodInfoError(YardstickException): + message = 'Missing pod args, please check' + + +class UnsupportedPodFormatError(YardstickException): + message = 'Failed to load pod info, unsupported format' diff --git a/yardstick/service/__init__.py b/yardstick/service/__init__.py new file mode 100644 index 000000000..1c3953de6 --- /dev/null +++ b/yardstick/service/__init__.py @@ -0,0 +1,12 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +class Service(object): + pass diff --git a/yardstick/service/environment.py b/yardstick/service/environment.py new file mode 100644 index 000000000..324589f79 --- /dev/null +++ b/yardstick/service/environment.py @@ -0,0 +1,101 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import tempfile +import logging +import collections + +from oslo_serialization import jsonutils + +from yardstick.service import Service +from yardstick.common.exceptions import MissingPodInfoError +from yardstick.common.exceptions import UnsupportedPodFormatError +from yardstick.common.ansible_common import AnsibleCommon + +LOG = logging.getLogger(__name__) + + +class Environment(Service): + def __init__(self, pod=None): + super(Environment, self).__init__() + # pod can be a dict or a json format string + self.pod = pod + + def get_sut_info(self): + temdir = tempfile.mkdtemp(prefix='sut') + + nodes = self._load_pod_info() + ansible = AnsibleCommon(nodes=nodes) + ansible.gen_inventory_ini_dict() + sut_info = ansible.get_sut_info(temdir) + + return self._format_sut_info(sut_info) + + def _load_pod_info(self): + if self.pod is None: + raise MissingPodInfoError + + if isinstance(self.pod, collections.Mapping): + try: + return self.pod['nodes'] + except KeyError: + raise UnsupportedPodFormatError + + try: + return jsonutils.loads(self.pod)['nodes'] + except (ValueError, KeyError): + raise UnsupportedPodFormatError + + def _format_sut_info(self, sut_info): + return {k: self._format_node_info(v) for k, v in sut_info.items()} + + def _format_node_info(self, node_info): + info = [] + facts = node_info.get('ansible_facts', {}) + + info.append(['hostname', facts.get('ansible_hostname')]) + + info.append(['product_name', facts.get('ansible_product_name')]) + info.append(['product_version', facts.get('ansible_product_version')]) + + processors = facts.get('ansible_processor', []) + try: + processor_type = '{} {}'.format(processors[0], processors[1]) + except IndexError: + LOG.exception('No Processor in SUT data') + processor_type = None + + info.append(['processor_type', processor_type]) + info.append(['architecture', facts.get('ansible_architecture')]) + info.append(['processor_cores', facts.get('ansible_processor_cores')]) + info.append(['processor_vcpus', facts.get('ansible_processor_vcpus')]) + + memory = facts.get('ansible_memtotal_mb') + memory = round(memory * 1.0 / 1024, 2) if memory else None + info.append(['memory', '{} GB'.format(memory)]) + + devices = facts.get('ansible_devices', {}) + info.extend([self._get_device_info(k, v) for k, v in devices.items()]) + + lsb_description = facts.get('ansible_lsb', {}).get('description') + info.append(['OS', lsb_description]) + + interfaces = facts.get('ansible_interfaces') + info.append(['interfaces', interfaces]) + if isinstance(interfaces, collections.Sequence): + info.extend([self._get_interface_info(facts, i) for i in interfaces]) + info = [i for i in info if i] + + return info + + def _get_interface_info(self, facts, name): + mac = facts.get('ansible_{}'.format(name), {}).get('macaddress') + return [name, mac] if mac else [] + + def _get_device_info(self, name, info): + return ['disk_{}'.format(name), info.get('size')] diff --git a/yardstick/tests/unit/service/__init__.py b/yardstick/tests/unit/service/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/yardstick/tests/unit/service/test_environment.py b/yardstick/tests/unit/service/test_environment.py new file mode 100644 index 000000000..4af9a3958 --- /dev/null +++ b/yardstick/tests/unit/service/test_environment.py @@ -0,0 +1,49 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest + +import mock + +from yardstick.service.environment import Environment +from yardstick.service.environment import AnsibleCommon +from yardstick.common.exceptions import UnsupportedPodFormatError + + +class EnvironmentTestCase(unittest.TestCase): + + def test_get_sut_info(self): + pod_info = { + 'nodes': [ + { + 'name': 'node1', + 'host_name': 'host1', + 'role': 'Controller', + 'ip': '10.1.0.50', + 'user': 'root', + 'passward': 'root' + } + ] + } + + AnsibleCommon.gen_inventory_ini_dict = mock.MagicMock() + AnsibleCommon.get_sut_info = mock.MagicMock(return_value={'node1': {}}) + + env = Environment(pod=pod_info) + env.get_sut_info() + + def test_get_sut_info_pod_str(self): + pod_info = 'nodes' + + env = Environment(pod=pod_info) + with self.assertRaises(UnsupportedPodFormatError): + env.get_sut_info() + + +if __name__ == '__main__': + unittest.main() -- cgit 1.2.3-korg