1 # Copyright (c) 2016-2017 Intel Corporation
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """ Add generic L3 forwarder implementation based on sample_vnf.py"""
16 from __future__ import absolute_import
22 from netaddr import IPRange
24 from six.moves import zip
26 from yardstick.benchmark.contexts.base import Context
27 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, \
30 LOG = logging.getLogger(__name__)
33 class RouterVNF(SampleVNF):
37 def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
38 if setup_env_helper_type is None:
39 setup_env_helper_type = DpdkVnfSetupEnvHelper
42 vnfd['mgmt-interface'].pop("pkey", "")
43 vnfd['mgmt-interface']['password'] = 'password'
45 super(RouterVNF, self).__init__(name, vnfd, setup_env_helper_type, resource_helper_type)
47 def instantiate(self, scenario_cfg, context_cfg):
48 self.scenario_helper.scenario_cfg = scenario_cfg
49 self.context_cfg = context_cfg
50 self.nfvi_context = Context.get_context_from_server(self.scenario_helper.nodes[self.name])
51 self.configure_routes(self.name, scenario_cfg, context_cfg)
53 def wait_for_instantiate(self):
54 time.sleep(self.WAIT_TIME)
57 # we can't share ssh paramiko objects to force new connection
58 self.ssh_helper.drop_connection()
62 self.resource_helper.stop_collect()
64 def scale(self, flavor=""):
68 def row_with_header(header, data):
69 """Returns dictionary per row of values for 'ip show stats'.
72 header(str): output header
73 data(str): output data
76 dict: dictionary per row of values for 'ip show stats'
79 prefix, columns = header.strip().split(':')
80 column_names = ["{0}:{1}".format(prefix, h) for h in columns.split()]
81 return dict(list(zip(column_names, data.strip().split())))
83 RX_TX_RE = re.compile(r"\s+[RT]X[^:]*:")
86 def get_stats(cls, stdout):
87 """Returns list of IP statistics.
90 stdout(str): command output
93 dict: list of IP statistics
96 input_lines = stdout.splitlines()
98 for n, row in enumerate(input_lines):
99 if cls.RX_TX_RE.match(row):
100 # use pairs of rows, header and data
101 table.update(cls.row_with_header(*input_lines[n:n + 2]))
104 def collect_kpi(self):
105 # Implement stats collection
106 ip_link_stats = '/sbin/ip -s link'
107 stdout = self.ssh_helper.execute(ip_link_stats)[1]
108 link_stats = self.get_stats(stdout)
109 # get RX/TX from link_stats and assign to results
113 "packets_dropped": 0,
115 "link_stats": link_stats
118 LOG.debug("%s collect KPIs %s", "RouterVNF", result)
123 def configure_routes(self, node_name, scenario_cfg, context_cfg):
124 # Configure IP of dataplane ports and add static ARP entries
126 # This function should be modified to configure a 3rd party/commercial VNF.
127 # The current implementation works on a Linux based VNF with "ip" command.
130 # {'src_ip': ['152.16.100.26-152.16.100.27'],
131 # 'dst_ip': ['152.16.40.26-152.16.40.27'], 'count': 2}
136 ip_cmd_replace = '/sbin/ip addr replace %s/24 dev %s'
137 ip_cmd_up = '/sbin/ip link set %s up'
138 ip_cmd_flush = '/sbin/ip address flush dev %s'
140 # Get VNF IPs from test case file
141 for value in context_cfg['nodes'][node_name]['interfaces'].values():
142 dst_macs.append(value['dst_mac'])
144 # Get the network interface name using local_mac
145 iname = self.ssh_helper.execute("/sbin/ip a |grep -B 1 %s | head -n 1"
146 % (value['local_mac']))
147 iname = iname[1].split(":")[1].strip()
150 self.ssh_helper.execute(ip_cmd_flush % iname)
152 # Get the local_ip from context_cfg and assign to the data ports
153 self.ssh_helper.execute(ip_cmd_replace % (str(value['local_ip']),
156 self.ssh_helper.execute(ip_cmd_up % iname)
157 time.sleep(self.INTERFACE_WAIT)
159 # Configure static ARP entries for each IP
160 # using SSH or REST API calls
162 src_ips = scenario_cfg['options']['flow']['src_ip']
163 dst_ips = scenario_cfg['options']['flow']['dst_ip']
165 raise KeyError("Missing flow definition in scenario section" +
166 " of the task definition file")
170 for src, dst in zip(src_ips, dst_ips):
171 range1 = itertools.cycle(iter(src.split('-')))
172 range2 = itertools.cycle(iter(dst.split('-')))
174 range1 = IPRange(next(range1), next(range1))
175 range2 = IPRange(next(range2), next(range2))
176 ip_ranges.append(range1)
177 ip_ranges.append(range2)
179 ip_cmd = '/sbin/ip neigh add %s lladdr %s dev %s nud perm'
180 for idx, iface in enumerate(ifaces):
181 for addr in ip_ranges[idx]:
182 self.ssh_helper.execute(ip_cmd % (addr, dst_macs[idx], iface))
184 arp_status = self.ssh_helper.execute("arp -a -n")
185 LOG.debug('arp %s', arp_status)