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.configure_routes(self.name, scenario_cfg, context_cfg)
52 def wait_for_instantiate(self):
53 time.sleep(self.WAIT_TIME)
56 # we can't share ssh paramiko objects to force new connection
57 self.ssh_helper.drop_connection()
61 self.resource_helper.stop_collect()
63 def scale(self, flavor=""):
67 def row_with_header(header, data):
68 """Returns dictionary per row of values for 'ip show stats'.
71 header(str): output header
72 data(str): output data
75 dict: dictionary per row of values for 'ip show stats'
78 prefix, columns = header.strip().split(':')
79 column_names = ["{0}:{1}".format(prefix, h) for h in columns.split()]
80 return dict(list(zip(column_names, data.strip().split())))
82 RX_TX_RE = re.compile(r"\s+[RT]X[^:]*:")
85 def get_stats(cls, stdout):
86 """Returns list of IP statistics.
89 stdout(str): command output
92 dict: list of IP statistics
95 input_lines = stdout.splitlines()
97 for n, row in enumerate(input_lines):
98 if cls.RX_TX_RE.match(row):
99 # use pairs of rows, header and data
100 table.update(cls.row_with_header(*input_lines[n:n + 2]))
103 def collect_kpi(self):
104 # Implement stats collection
105 ip_link_stats = '/sbin/ip -s link'
106 stdout = self.ssh_helper.execute(ip_link_stats)[1]
107 link_stats = self.get_stats(stdout)
108 # get RX/TX from link_stats and assign to results
109 physical_node = Context.get_physical_node_from_server(
110 self.scenario_helper.nodes[self.name])
113 "physical_node": physical_node,
115 "packets_dropped": 0,
117 "link_stats": link_stats
120 LOG.debug("%s collect KPIs %s", "RouterVNF", result)
125 def configure_routes(self, node_name, scenario_cfg, context_cfg):
126 # Configure IP of dataplane ports and add static ARP entries
128 # This function should be modified to configure a 3rd party/commercial VNF.
129 # The current implementation works on a Linux based VNF with "ip" command.
132 # {'src_ip': ['152.16.100.26-152.16.100.27'],
133 # 'dst_ip': ['152.16.40.26-152.16.40.27'], 'count': 2}
138 ip_cmd_replace = '/sbin/ip addr replace %s/24 dev %s'
139 ip_cmd_up = '/sbin/ip link set %s up'
140 ip_cmd_flush = '/sbin/ip address flush dev %s'
142 # Get VNF IPs from test case file
143 for value in context_cfg['nodes'][node_name]['interfaces'].values():
144 dst_macs.append(value['dst_mac'])
146 # Get the network interface name using local_mac
147 iname = self.ssh_helper.execute("/sbin/ip a |grep -B 1 %s | head -n 1"
148 % (value['local_mac']))
149 iname = iname[1].split(":")[1].strip()
152 self.ssh_helper.execute(ip_cmd_flush % iname)
154 # Get the local_ip from context_cfg and assign to the data ports
155 self.ssh_helper.execute(ip_cmd_replace % (str(value['local_ip']),
158 self.ssh_helper.execute(ip_cmd_up % iname)
159 time.sleep(self.INTERFACE_WAIT)
161 # Configure static ARP entries for each IP
162 # using SSH or REST API calls
164 src_ips = scenario_cfg['options']['flow']['src_ip']
165 dst_ips = scenario_cfg['options']['flow']['dst_ip']
167 raise KeyError("Missing flow definition in scenario section" +
168 " of the task definition file")
172 for src, dst in zip(src_ips, dst_ips):
173 range1 = itertools.cycle(iter(src.split('-')))
174 range2 = itertools.cycle(iter(dst.split('-')))
176 range1 = IPRange(next(range1), next(range1))
177 range2 = IPRange(next(range2), next(range2))
178 ip_ranges.append(range1)
179 ip_ranges.append(range2)
181 ip_cmd = '/sbin/ip neigh add %s lladdr %s dev %s nud perm'
182 for idx, iface in enumerate(ifaces):
183 for addr in ip_ranges[idx]:
184 self.ssh_helper.execute(ip_cmd % (addr, dst_macs[idx], iface))
186 arp_status = self.ssh_helper.execute("arp -a -n")
187 LOG.debug('arp %s', arp_status)