From c9cd093f2f441adc9dd33627255326008e021a67 Mon Sep 17 00:00:00 2001 From: Martin Klozik Date: Tue, 16 Aug 2016 14:59:05 +0100 Subject: multi VM: Multi VMs in serial or parallel Support for deployment scenarios with any number of VMs in both serial and parallel configuration. Detailed content of the patch: * VswitchControllerPXP class for multi VM support * pvvpxx and pvpvxx deployments for xx VMs in serial respective parallel configuration * special GUEST_ options expansion to requested number of VMs; * support of GUEST_ options specific macros #VMINDEX, #MAC(), #IP() and #EVAL() * all GUEST specific options are turned to lists to be VM specific * support for VM with 1 NIC * support for VM with multiple NIC pairs; traffic is routed in serial or parallel between NIC paris based on deployment scenario * support for PVVP and PVPV scenarios using VMs with different numbers of NICs JIRA: VSPERF-361 Change-Id: I05bedbdfa9a81ea0166d9b03d83ae49d6cb8b19b Signed-off-by: Martin Klozik Reviewed-by: Maryam Tahhan Reviewed-by: Al Morton Reviewed-by: Christian Trautman Reviewed-by: Bill Michalowski Reviewed-by: Antonio Fischetti --- conf/__init__.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) (limited to 'conf/__init__.py') diff --git a/conf/__init__.py b/conf/__init__.py index 46228235..88e8cec6 100644 --- a/conf/__init__.py +++ b/conf/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2015 Intel Corporation. +# Copyright 2015-2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,7 +22,22 @@ and any user provided settings file. import os import re +import logging import pprint +import ast +import netaddr + +_LOGGER = logging.getLogger(__name__) + +# regex to parse configuration macros from 04_vnf.conf +# it will select all patterns starting with # sign +# and returns macro parameters and step +# examples of valid macros: +# #VMINDEX +# #MAC(AA:BB:CC:DD:EE:FF) or #MAC(AA:BB:CC:DD:EE:FF,2) +# #IP(192.168.1.2) or #IP(192.168.1.2,2) +# #EVAL(2*#VMINDEX) +_PARSE_PATTERN = r'(#[A-Z]+)(\(([^(),]+)(,([0-9]+))?\))?' class Settings(object): """Holding class for settings. @@ -121,6 +136,64 @@ class Settings(object): for key in os.environ: setattr(self, key, os.environ[key]) + def check_vm_settings(self, vm_number): + """ + Check all VM related settings starting with GUEST_ prefix. + If it is not available for defined number of VMs, then vsperf + will try to expand it automatically. Expansion is performed + also in case that first list item contains a macro. + """ + for key in self.__dict__: + if key.startswith('GUEST_'): + if (isinstance(self.__dict__[key], str) and + self.__dict__[key].find('#') >= 0): + self.__dict__[key] = [self.__dict__[key]] + self._expand_vm_settings(key, 1) + self.__dict__[key] = self.__dict__[key][0] + + if isinstance(self.__dict__[key], list): + if (len(self.__dict__[key]) < vm_number or + str(self.__dict__[key][0]).find('#') >= 0): + # expand configuration for all VMs + self._expand_vm_settings(key, vm_number) + + def _expand_vm_settings(self, key, vm_number): + """ + Expand VM option with given key for given number of VMs + """ + master_value = self.__dict__[key][0] + master_value_str = str(master_value) + if master_value_str.find('#') >= 0: + self.__dict__[key] = [] + for vmindex in range(vm_number): + value = master_value_str.replace('#VMINDEX', str(vmindex)) + for macro, args, param, _, step in re.findall(_PARSE_PATTERN, value): + multi = int(step) if len(step) and int(step) else 1 + if macro == '#EVAL': + tmp_result = str(eval(param)) + elif macro == '#MAC': + mac_value = netaddr.EUI(param).value + mac = netaddr.EUI(mac_value + vmindex * multi) + mac.dialect = netaddr.mac_unix_expanded + tmp_result = str(mac) + elif macro == '#IP': + ip_value = netaddr.IPAddress(param).value + tmp_result = str(netaddr.IPAddress(ip_value + vmindex * multi)) + else: + raise RuntimeError('Unknown configuration macro {} in {}'.format(macro, key)) + + value = value.replace("{}{}".format(macro, args), tmp_result) + + # retype value to original type if needed + if not isinstance(master_value, str): + value = ast.literal_eval(value) + self.__dict__[key].append(value) + else: + for vmindex in range(len(self.__dict__[key]), vm_number): + self.__dict__[key].append(master_value) + + _LOGGER.debug("Expanding option: %s = %s", key, self.__dict__[key]) + def __str__(self): """Provide settings as a human-readable string. -- cgit 1.2.3-korg