#!/usr/bin/env python # # Copyright 2014 Huawei Technologies Co. Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """binary to deploy a cluster by compass client api.""" import os import re import socket import sys import time import yaml import netaddr import requests import json import itertools from collections import defaultdict from restful import Client ROLE_UNASSIGNED = True ROLE_ASSIGNED = False import log as logging LOG = logging.getLogger(__name__) from oslo_config import cfg CONF = cfg.CONF def byteify(input): if isinstance(input, dict): return dict([(byteify(key),byteify(value)) for key,value in input.iteritems()]) elif isinstance(input, list): return [byteify(element) for element in input] elif isinstance(input, unicode): return input.encode('utf-8') else: return input opts = [ cfg.StrOpt('compass_server', help='compass server url', default='http://127.0.0.1/api'), cfg.StrOpt('compass_user_email', help='compass user email', default='admin@huawei.com'), cfg.StrOpt('compass_user_password', help='compass user password', default='admin'), cfg.StrOpt('switch_ips', help='comma seperated switch ips', default=''), cfg.StrOpt('switch_credential', help='comma separated =', default='version=2c,community=public'), cfg.IntOpt('switch_max_retries', help='max retries of poll switch', default=10), cfg.IntOpt('switch_retry_interval', help='interval to repoll switch', default=10), cfg.BoolOpt('poll_switches', help='if the client polls switches', default=True), cfg.StrOpt('machines', help='comma separated mac addresses of machines', default=''), cfg.StrOpt('subnets', help='comma seperated subnets', default=''), cfg.StrOpt('adapter_name', help='adapter name', default=''), cfg.StrOpt('adapter_os_pattern', help='adapter os name', default=r'^(?i)centos.*'), cfg.StrOpt('adapter_target_system_pattern', help='adapter target system name', default='^openstack$'), cfg.StrOpt('adapter_flavor_pattern', help='adapter flavor name', default='allinone'), cfg.StrOpt('cluster_name', help='cluster name', default='cluster1'), cfg.StrOpt('language', help='language', default='EN'), cfg.StrOpt('timezone', help='timezone', default='GMT'), cfg.StrOpt('http_proxy', help='http proxy', default=''), cfg.StrOpt('https_proxy', help='https proxy', default=''), cfg.StrOpt('no_proxy', help='no proxy', default=''), cfg.StrOpt('ntp_server', help='ntp server', default=''), cfg.StrOpt('dns_servers', help='dns servers', default=''), cfg.StrOpt('domain', help='domain', default=''), cfg.StrOpt('search_path', help='search path', default=''), cfg.StrOpt('local_repo_url', help='local repo url', default=''), cfg.StrOpt('default_gateway', help='default gateway', default=''), cfg.StrOpt('server_credential', help=( 'server credential formatted as ' '=' ), default='root=root'), cfg.StrOpt('os_config_json_file', help='json formatted os config file', default=''), cfg.StrOpt('service_credentials', help=( 'comma seperated service credentials formatted as ' ':=,...' ), default=''), cfg.StrOpt('console_credentials', help=( 'comma seperated console credential formated as ' ':=' ), default=''), cfg.StrOpt('hostnames', help='comma seperated hostname', default=''), cfg.StrOpt('host_networks', help=( 'semicomma seperated host name and its networks ' ':=||,...' ), default=''), cfg.StrOpt('partitions', help=( 'comma seperated partitions ' '=' ), default='tmp:percentage=10%,var:percentage=30%,home:percentage=30%'), cfg.StrOpt('network_mapping', help=( 'comma seperated network mapping ' '=' ), default=''), cfg.StrOpt('package_config_json_file', help='json formatted os config file', default=''), cfg.StrOpt('host_roles', help=( 'semicomma separated host roles ' '='
.. This work is licensed under a Creative Commons Attribution 4.0 International
.. License.
.. http://creativecommons.org/licenses/by/4.0
.. (c) OPNFV, Huawei Technologies Co.,Ltd and others.

*************************************
Yardstick Test Case Description TC040
*************************************

.. _Parser: https://wiki.opnfv.org/parser

+-----------------------------------------------------------------------------+
|Verify Parser Yang-to-Tosca                                                  |
|                                                                             |
+--------------+--------------------------------------------------------------+
|test case id  | OPNFV_YARDSTICK_TC040 Verify Parser Yang-to-Tosca            |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|metric        | 1. tosca file which is converted from yang file by Parser    |
|              | 2. result whether the output is same with expected outcome   |
+--------------+--------------------------------------------------------------+
|test purpose  | To verify the function of Yang-to-Tosca in Parser.           |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|configuration | file: opnfv_yardstick_tc040.yaml                             |
|              |                                                              |
|              | yangfile: the path of the yangfile which you want to convert |
|              | toscafile: the path of the toscafile which is your expected  |
|              | outcome.                                                     |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|test tool     | Parser                                                       |
|              |                                                              |
|              | (Parser is not part of a Linux distribution, hence it        |
|              | needs to be installed. As an example see the                 |
|              | /yardstick/benchmark/scenarios/parser/parser_setup.sh for    |
|              | how to install it manual. Of course, it will be installed    |
|              | and uninstalled automatically when you run this test case    |
|              | by yardstick)                                                |
+--------------+--------------------------------------------------------------+
|references    | Parser_                                                      |
|              |                                                              |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|applicability | Test can be configured with different path of yangfile and   |
|              | toscafile to fit your real environment to verify Parser      |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|pre-test      |  No POD specific requirements have been identified.          |
|conditions    |  it can be run without VM                                    |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|test sequence | description and expected result                              |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|step 1        | parser is installed without VM, running Yang-to-Tosca module |
|              | to convert yang file to tosca file, validating output against|
|              | expected outcome.                                            |
|              |                                                              |
|              | Result: Logs are stored.                                     |
+--------------+--------------------------------------------------------------+
|test verdict  | Fails only if output is different with expected outcome      |
|              | or if there is a test case execution problem.                |
|              |                                                              |
+--------------+--------------------------------------------------------------+
sole credential %s' % console_credential ) console_name, console_pair = console_credential.split(':', 1) if '=' not in console_pair: raise Exception( 'there is no = in console %s security' % console_name ) username, password = console_pair.split('=', 1) console_credential_cfg[console_name] = { 'username': username, 'password': password } package_config["security"] = {"service_credentials": service_credential_cfg, "console_credentials": console_credential_cfg} network_mapping = dict([ network_pair.split('=', 1) for network_pair in CONF.network_mapping.split(',') if '=' in network_pair ]) package_config['network_mapping'] = network_mapping assert(os.path.exists(CONF.network_cfg)) network_cfg = yaml.load(open(CONF.network_cfg)) package_config["network_cfg"] = network_cfg assert(os.path.exists(CONF.neutron_cfg)) neutron_cfg = yaml.load(open(CONF.neutron_cfg)) package_config["neutron_config"] = neutron_cfg """ package_config_filename = CONF.package_config_json_file if package_config_filename: util.merge_dict( package_config, _load_config(package_config_filename) ) """ package_config['ha_proxy'] = {} #TODO, we need two vip if CONF.cluster_pub_vip: package_config["ha_proxy"]["pub_vip"] = CONF.cluster_pub_vip if CONF.cluster_prv_vip: package_config["ha_proxy"]["prv_vip"] = CONF.cluster_prv_vip if CONF.cluster_vip: package_config["ha_proxy"]["vip"] = CONF.cluster_vip status, resp = self.client.update_cluster_config( cluster_id, package_config=package_config) LOG.info( 'set package config %s to cluster %s status: %s, resp: %s', package_config, cluster_id, status, resp) if not self.is_ok(status): raise RuntimeError("set cluster package_config failed") def set_host_roles(self, cluster_id, host_id, roles): status, response = self.client.update_cluster_host( cluster_id, host_id, roles=roles) LOG.info( 'set cluster %s host %s roles %s status %s: %s', cluster_id, host_id, roles, status, response ) if not self.is_ok(status): raise RuntimeError("set host roles failed") for role in roles: if role in self.role_mapping: self.role_mapping[role] = ROLE_ASSIGNED def set_all_hosts_roles(self, cluster_id): for host_str in CONF.host_roles.split(';'): host_str = host_str.strip() hostname, roles_str = host_str.split('=', 1) assert(hostname in self.host_mapping) host_id = self.host_mapping[hostname] roles = [role.strip() for role in roles_str.split(',') if role] self.set_host_roles(cluster_id, host_id, roles) self.host_roles[hostname] = roles unassigned_hostnames = list(set(self.host_mapping.keys()) \ - set(self.host_roles.keys())) unassigned_roles = [ role for role, status in self.role_mapping.items() if is_role_unassigned(status)] assert(len(unassigned_hostnames) >= len(unassigned_roles)) for hostname, role in map(None, unassigned_hostnames, unassigned_roles): host_id = self.host_mapping[hostname] self.set_host_roles(cluster_id, host_id, [role]) self.host_roles[hostname] = [role] unassigned_hostnames = list(set(self.host_mapping.keys()) \ - set(self.host_roles.keys())) if not unassigned_hostnames: return # assign default roles to unassigned hosts default_roles = [ role for role in CONF.default_roles.split(',') if role ] assert(default_roles) cycle_roles = itertools.cycle(default_roles) for hostname in unassigned_hostnames: host_id = self.host_mapping[hostname] roles = [cycle_roles.next()] self.set_host_roles(cluster_id, host_id, roles) self.host_roles[hostname] = roles def deploy_clusters(self, cluster_id): host_ids = self.host_mapping.values() status, response = self.client.review_cluster( cluster_id, review={'hosts': host_ids} ) LOG.info( 'review cluster %s hosts %s, status %s: %s', cluster_id, host_ids, status, response ) #TODO, what this doning? if not self.is_ok(status): raise RuntimeError("review cluster host failed") status, response = self.client.deploy_cluster( cluster_id, deploy={'hosts': host_ids} ) LOG.info( 'deploy cluster %s hosts %s status %s: %s', cluster_id, host_ids, status, response ) if not self.is_ok(status): raise RuntimeError("deploy cluster failed") def get_installing_progress(self, cluster_id): """get intalling progress.""" action_timeout = time.time() + 60 * float(CONF.action_timeout) deployment_timeout = time.time() + 60 * float( CONF.deployment_timeout) current_time = time.time() deployment_failed = True while current_time < deployment_timeout: status, cluster_state = self.client.get_cluster_state(cluster_id) LOG.info( 'get cluster %s state status %s: %s', cluster_id, status, cluster_state ) if not self.is_ok(status): raise RuntimeError("can not get cluster state") if cluster_state['state'] in ['UNINITIALIZED', 'INITIALIZED']: if current_time >= action_timeout: deployment_failed = True break else: continue elif cluster_state['state'] == 'SUCCESSFUL': deployment_failed = False break elif cluster_state['state'] == 'ERROR': deployment_failed = True break if deployment_failed: raise RuntimeError("deploy cluster failed") def check_dashboard_links(self, cluster_id): dashboard_url = CONF.dashboard_url if not dashboard_url: LOG.info('no dashboarde url set') return dashboard_link_pattern = re.compile( CONF.dashboard_link_pattern) r = requests.get(dashboard_url, verify=False) r.raise_for_status() match = dashboard_link_pattern.search(r.text) if match: LOG.info( 'dashboard login page for cluster %s can be downloaded', cluster_id) else: msg = ( '%s failed to be downloaded\n' 'the context is:\n%s\n' ) % (dashboard_url, r.text) raise Exception(msg) def main(): client = CompassClient() machines = client.get_machines() LOG.info('machines are %s', machines) client.add_subnets() adapter_id, os_id, distributed_system_id, flavor_id = client.get_adapter() cluster_id = client.add_cluster(adapter_id, os_id, flavor_id) client.add_cluster_hosts(cluster_id, machines) client.set_host_networking() client.set_cluster_os_config(cluster_id) if distributed_system_id: client.set_cluster_package_config(cluster_id) client.set_all_hosts_roles(cluster_id) client.deploy_clusters(cluster_id) client.get_installing_progress(cluster_id) client.check_dashboard_links(cluster_id) if __name__ == "__main__": CONF(args=sys.argv[1:]) main()