From 0a56dfd7f42d6a6d849e5cf3f82b0863c8a62ffe Mon Sep 17 00:00:00 2001 From: Shuya Nakama Date: Fri, 25 Aug 2017 14:26:30 +0000 Subject: Refactor the vyos_vrouter to adopt VNF abstraction JIRA: FUNCTEST-788 1.Modifying code of vyos_vrouter to inherit vnf abstraction class. 2.Adding vyos_vrouter code from our repo to functest. 3.Adding unit test of vyos_vrouter. 4.Doing test of modified vyos_vrouter codes on our labs. Change-Id: I77e4be8b2a140ea0176c607f2be736599f893ace Signed-off-by: Shuya Nakama --- .../vnf/router/vnf_controller/__init__.py | 0 .../vnf/router/vnf_controller/checker.py | 59 ++++++++ .../vnf/router/vnf_controller/command_generator.py | 32 +++++ .../vnf/router/vnf_controller/ssh_client.py | 131 ++++++++++++++++++ .../vnf/router/vnf_controller/vm_controller.py | 148 +++++++++++++++++++++ .../vnf/router/vnf_controller/vnf_controller.py | 136 +++++++++++++++++++ 6 files changed, 506 insertions(+) create mode 100644 functest/opnfv_tests/vnf/router/vnf_controller/__init__.py create mode 100644 functest/opnfv_tests/vnf/router/vnf_controller/checker.py create mode 100644 functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py create mode 100644 functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py create mode 100644 functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py create mode 100644 functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py (limited to 'functest/opnfv_tests/vnf/router/vnf_controller') diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/__init__.py b/functest/opnfv_tests/vnf/router/vnf_controller/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/checker.py b/functest/opnfv_tests/vnf/router/vnf_controller/checker.py new file mode 100644 index 000000000..198a5ffc9 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/checker.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vrouter test result check module""" + +import json +import logging +import re + +from jinja2 import Environment, FileSystemLoader + + +class Checker(object): + """vrouter test result check class""" + + logger = logging.getLogger(__name__) + + def __init__(self): + self.logger.debug("init checker") + + def load_check_rule(self, rule_file_dir, rule_file_name, parameter): + loader = FileSystemLoader(rule_file_dir, + encoding='utf8') + env = Environment(loader=loader) + check_rule_template = env.get_template(rule_file_name) + check_rule = check_rule_template.render(parameter) + check_rule_data = json.loads(check_rule) + return check_rule_data + + def regexp_information(self, response, rules): + status = False + result_data = {} + + for rule in rules["rules"]: + result_data = { + "test_name": rule["description"], + "result": "NG" + } + + match = re.search(rule["regexp"], + response) + rule["response"] = response + if match is None: + status = False + break + + if not match.group(1) == rule["result"]: + status = False + else: + result_data["result"] = "OK" + status = True + + return status, result_data diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py b/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py new file mode 100644 index 000000000..98cb14cc0 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/command_generator.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""command generator module for vrouter testing""" + +import logging +from jinja2 import Environment, FileSystemLoader + + +class CommandGenerator(object): + """command generator class for vrouter testing""" + + logger = logging.getLogger(__name__) + + def __init__(self): + self.logger.debug("init command generator") + + def load_template(self, template_dir, template): + loader = FileSystemLoader(template_dir, + encoding='utf8') + env = Environment(loader=loader) + return env.get_template(template) + + def command_create(self, template, parameter): + commands = template.render(parameter) + return commands.split('\n') diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py new file mode 100644 index 000000000..c85a57351 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/ssh_client.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""ssh client module for vrouter testing""" + +import logging +import paramiko +import time +import yaml + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf + +RECEIVE_ROOP_WAIT = 1 + +DEFAULT_CONNECT_TIMEOUT = 10 +DEFAULT_CONNECT_RETRY_COUNT = 10 +DEFAULT_SEND_TIMEOUT = 10 + + +class SshClient(object): + """ssh client class for vrouter testing""" + + logger = logging.getLogger(__name__) + + def __init__(self, ip_address, user, password=None, key_filename=None): + self.ip_address = ip_address + self.user = user + self.password = password + self.key_filename = key_filename + self.connected = False + self.shell = None + + self.logger.setLevel(logging.INFO) + + self.ssh = paramiko.SSHClient() + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + self.util = Utilvnf() + with open(self.util.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.ssh_revieve_buff = test_env_config_yaml.get("general").get( + "ssh_receive_buffer") + + def connect(self, time_out=DEFAULT_CONNECT_TIMEOUT, + retrycount=DEFAULT_CONNECT_RETRY_COUNT): + while retrycount > 0: + try: + self.logger.info("SSH connect to %s.", self.ip_address) + self.ssh.connect(self.ip_address, + username=self.user, + password=self.password, + key_filename=self.key_filename, + timeout=time_out, + look_for_keys=False, + allow_agent=False) + + self.logger.info("SSH connection established to %s.", + self.ip_address) + + self.shell = self.ssh.invoke_shell() + + while not self.shell.recv_ready(): + time.sleep(RECEIVE_ROOP_WAIT) + + self.shell.recv(self.ssh_revieve_buff) + break + except: # pylint: disable=broad-except + self.logger.info("SSH timeout for %s...", self.ip_address) + time.sleep(time_out) + retrycount -= 1 + + if retrycount == 0: + self.logger.error("Cannot establish connection to IP '%s'. " + + "Aborting", + self.ip_address) + self.connected = False + return self.connected + + self.connected = True + return self.connected + + def send(self, cmd, prompt, timeout=DEFAULT_SEND_TIMEOUT): + if self.connected is True: + self.shell.settimeout(timeout) + self.logger.debug("Commandset : '%s'", cmd) + + try: + self.shell.send(cmd + '\n') + except: # pylint: disable=broad-except + self.logger.error("ssh send timeout : Command : '%s'", cmd) + return None + + res_buff = '' + while not res_buff.endswith(prompt): + time.sleep(RECEIVE_ROOP_WAIT) + try: + res = self.shell.recv(self.ssh_revieve_buff) + except: # pylint: disable=broad-except + self.logger.error("ssh receive timeout : Command : '%s'", + cmd) + break + + res_buff += res + + self.logger.debug("Response : '%s'", res_buff) + return res_buff + else: + self.logger.error("Cannot connected to IP '%s'.", self.ip_address) + return None + + def close(self): + if self.connected is True: + self.ssh.close() + + def error_check(response, err_strs=["error", + "warn", + "unknown command", + "already exist"]): + for err in err_strs: + if err in response: + return False + + return True diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py b/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py new file mode 100644 index 000000000..cd228fe26 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/vm_controller.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vm controll module""" + +import logging +import os +import time +import yaml + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf +from functest.opnfv_tests.vnf.router.vnf_controller.command_generator import ( + CommandGenerator) +from functest.opnfv_tests.vnf.router.vnf_controller.ssh_client import ( + SshClient) + + +class VmController(object): + """vm controll class""" + + logger = logging.getLogger(__name__) + + def __init__(self, util_info): + self.logger.debug("initialize vm controller") + self.command_gen = CommandGenerator() + credentials = util_info["credentials"] + + self.util = Utilvnf() + self.util.set_credentials(credentials["username"], + credentials["password"], + credentials["auth_url"], + credentials["tenant_name"], + credentials["region_name"]) + + with open(self.util.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.reboot_wait = test_env_config_yaml.get("general").get( + "reboot_wait") + self.command_wait = test_env_config_yaml.get("general").get( + "command_wait") + self.ssh_connect_timeout = test_env_config_yaml.get("general").get( + "ssh_connect_timeout") + self.ssh_connect_retry_count = test_env_config_yaml.get("general").get( + "ssh_connect_retry_count") + + def command_gen_from_template(self, command_file_path, cmd_input_param): + (command_file_dir, command_file_name) = os.path.split( + command_file_path) + template = self.command_gen.load_template(command_file_dir, + command_file_name) + return self.command_gen.command_create(template, + cmd_input_param) + + def config_vm(self, vm_info, test_cmd_file_path, + cmd_input_param, prompt_file_path): + ssh = self.connect_ssh_and_config_vm(vm_info, + test_cmd_file_path, + cmd_input_param, + prompt_file_path) + if ssh is None: + return False + + ssh.close() + + return True + + def connect_ssh_and_config_vm(self, vm_info, test_cmd_file_path, + cmd_input_param, prompt_file_path): + + key_filename = None + if "key_path" in vm_info: + key_filename = vm_info["key_path"] + + ssh = SshClient(ip_address=vm_info["floating_ip"], + user=vm_info["user"], + password=vm_info["pass"], + key_filename=key_filename) + + result = ssh.connect(self.ssh_connect_timeout, + self.ssh_connect_retry_count) + if not result: + self.logger.debug("try to vm reboot.") + self.util.reboot_vm(vm_info["vnf_name"]) + time.sleep(self.reboot_wait) + result = ssh.connect(self.ssh_connect_timeout, + self.ssh_connect_retry_count) + if not result: + return None + + (result, _) = self.command_create_and_execute( + ssh, + test_cmd_file_path, + cmd_input_param, + prompt_file_path) + if not result: + ssh.close() + return None + + return ssh + + def command_create_and_execute(self, ssh, test_cmd_file_path, + cmd_input_param, prompt_file_path): + prompt_file = open(prompt_file_path, + 'r') + prompt = yaml.safe_load(prompt_file) + prompt_file.close() + config_mode_prompt = prompt["config_mode"] + + commands = self.command_gen_from_template(test_cmd_file_path, + cmd_input_param) + return self.command_list_execute(ssh, + commands, + config_mode_prompt) + + def command_list_execute(self, ssh, command_list, prompt): + res_data_list = [] + for command in command_list: + self.logger.debug("Command : " + command) + (res, res_data) = self.command_execute(ssh, + command, + prompt) + self.logger.debug("Response : " + res_data) + res_data_list.append(res_data) + if not res: + return res, res_data_list + + time.sleep(self.command_wait) + + return True, res_data_list + + def command_execute(self, ssh, command, prompt): + res_data = ssh.send(command, prompt) + if res_data is None: + self.logger.info("retry send command : " + command) + res_data = ssh.send(command, + prompt) + if not ssh.error_check(res_data): + return False, res_data + + return True, res_data diff --git a/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py b/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py new file mode 100644 index 000000000..814e9e333 --- /dev/null +++ b/functest/opnfv_tests/vnf/router/vnf_controller/vnf_controller.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python + +# Copyright (c) 2017 Okinawa Open Laboratory 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 + +"""vrouter controll module""" + +import logging +import os +import prettytable +import time +import yaml + +from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf +from functest.opnfv_tests.vnf.router.vnf_controller.checker import Checker +from functest.opnfv_tests.vnf.router.vnf_controller.ssh_client import ( + SshClient) +from functest.opnfv_tests.vnf.router.vnf_controller.vm_controller import ( + VmController) + + +class VnfController(object): + """vrouter controll class""" + + logger = logging.getLogger(__name__) + + def __init__(self, util_info): + self.logger.debug("init vnf controller") + self.util = Utilvnf() + self.vm_controller = VmController(util_info) + + with open(self.util.test_env_config_yaml) as file_fd: + test_env_config_yaml = yaml.safe_load(file_fd) + file_fd.close() + + self.cmd_wait = test_env_config_yaml.get("general").get("command_wait") + self.ssh_connect_timeout = test_env_config_yaml.get("general").get( + "ssh_connect_timeout") + self.ssh_connect_retry_count = test_env_config_yaml.get("general").get( + "ssh_connect_retry_count") + + def config_vnf(self, source_vnf, destination_vnf, test_cmd_file_path, + parameter_file_path, prompt_file_path): + parameter_file = open(parameter_file_path, + 'r') + cmd_input_param = yaml.safe_load(parameter_file) + parameter_file.close() + + cmd_input_param["macaddress"] = source_vnf["data_plane_network_mac"] + cmd_input_param["source_ip"] = source_vnf["data_plane_network_ip"] + cmd_input_param["destination_ip"] = destination_vnf[ + "data_plane_network_ip"] + + return self.vm_controller.config_vm(source_vnf, + test_cmd_file_path, + cmd_input_param, + prompt_file_path) + + def result_check(self, target_vnf, reference_vnf, + check_rule_file_path_list, parameter_file_path, + prompt_file_path): + + res_dict_data_list = [] + + parameter_file = open(parameter_file_path, + 'r') + cmd_input_param = yaml.safe_load(parameter_file) + parameter_file.close() + + cmd_input_param["source_ip"] = target_vnf["data_plane_network_ip"] + cmd_input_param["destination_ip"] = reference_vnf[ + "data_plane_network_ip"] + + prompt_file = open(prompt_file_path, + 'r') + prompt = yaml.safe_load(prompt_file) + prompt_file.close() + terminal_mode_prompt = prompt["terminal_mode"] + + ssh = SshClient(target_vnf["floating_ip"], + target_vnf["user"], + target_vnf["pass"]) + + result = ssh.connect(self.ssh_connect_timeout, + self.ssh_connect_retry_count) + if not result: + return False, res_dict_data_list + + checker = Checker() + + res_table = prettytable.PrettyTable( + header_style='upper', padding_width=5, + field_names=['test item', 'result']) + + status = True + res_data_list = [] + for check_rule_file_path in check_rule_file_path_list: + (check_rule_dir, check_rule_file) = os.path.split( + check_rule_file_path) + check_rules = checker.load_check_rule(check_rule_dir, + check_rule_file, + cmd_input_param) + (res, res_data) = self.vm_controller.command_execute( + ssh, + check_rules["command"], + terminal_mode_prompt) + res_data_list.append(res_data) + if not res: + status = False + break + + (res, res_dict_data) = checker.regexp_information(res_data, + check_rules) + res_dict_data_list.append(res_dict_data) + res_table.add_row([res_dict_data["test_name"], + res_dict_data["result"]]) + if not res: + status = False + + time.sleep(self.cmd_wait) + + ssh.close() + + self.logger.info("Test result:\n\n%s\n", res_table.get_string()) + + self.output_check_result_detail_data(res_data_list) + + return status, res_dict_data_list + + def output_check_result_detail_data(self, res_data_list): + for res_data in res_data_list: + self.logger.debug(res_data) -- cgit 1.2.3-korg