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 """ vPE (Power Edge router) VNF model definitions based on IETS Spec """
16 from __future__ import absolute_import
17 from __future__ import print_function
23 from multiprocessing import Queue
24 import multiprocessing
28 from yardstick import ssh
29 from yardstick.network_services.vnf_generic.vnf.base import GenericVNF
30 from yardstick.network_services.utils import provision_tool
31 from yardstick.network_services.vnf_generic.vnf.base import QueueFileWrapper
32 from yardstick.network_services.nfvi.resource import ResourceProfile
34 LOG = logging.getLogger(__name__)
35 VPE_PIPELINE_COMMAND = '{tool_path} -p 0x3 -f {cfg_file} -s {script}'
36 CORES = ['0', '1', '2']
40 class VpeApproxVnf(GenericVNF):
41 """ This class handles vPE VNF model-driver definitions """
43 def __init__(self, vnfd):
44 super(VpeApproxVnf, self).__init__(vnfd)
49 self._vnf_process = None
50 self.connection = None
53 def _resource_collect_start(self):
54 self.resource.initiate_systemagent(self.bin_path)
57 def _resource_collect_stop(self):
60 def _collect_resource_kpi(self):
63 status = self.resource.check_if_sa_running("collectd")[0]
65 result = self.resource.amqp_collect_nfvi_kpi()
67 result = {"core": result}
72 def __setup_hugepages(cls, connection):
75 "awk '/Hugepagesize/ { print $2$3 }' < /proc/meminfo")[1]
76 hugepages = hugepages.rstrip()
79 '/sys/kernel/mm/hugepages/hugepages-%s/nr_hugepages' % hugepages
80 connection.execute("awk -F: '{ print $1 }' < %s" % memory_path)
82 pages = 16384 if hugepages.rstrip() == "2048kB" else 16
83 connection.execute("echo %s > %s" % (pages, memory_path))
85 def setup_vnf_environment(self, connection):
86 ''' setup dpdk environment needed for vnf to run '''
88 self.__setup_hugepages(connection)
89 connection.execute("modprobe uio && modprobe igb_uio")
91 exit_status = connection.execute("lsmod | grep -i igb_uio")[0]
95 dpdk = os.path.join(self.bin_path, "dpdk-16.07")
97 provision_tool(self.connection,
98 os.path.join(self.bin_path, "nsb_setup.sh"))
99 status = connection.execute("ls {} >/dev/null 2>&1".format(dpdk))[0]
101 connection.execute("bash %s dpdk >/dev/null 2>&1" % dpdk_setup)
103 def _get_cpu_sibling_list(self):
107 "/sys/devices/system/cpu/cpu%s/topology/thread_siblings_list" \
110 self.connection.execute("awk -F: '{ print $1 }' < %s" %
113 [(idx) if idx.isdigit() else idx for idx in cpuid.split(',')]
115 return [cpu.strip() for cpu in cpu_topo]
117 def scale(self, flavor=""):
118 ''' scale vnfbased on flavor input '''
119 super(VpeApproxVnf, self).scale(flavor)
121 def instantiate(self, scenario_cfg, context_cfg):
122 vnf_cfg = scenario_cfg['vnf_options']['vpe']['cfg']
124 mgmt_interface = self.vnfd["mgmt-interface"]
125 self.connection = ssh.SSH.from_node(mgmt_interface)
127 self.tc_file_name = '{0}.yaml'.format(scenario_cfg['tc'])
129 self.setup_vnf_environment(self.connection)
131 cores = self._get_cpu_sibling_list()
132 self.resource = ResourceProfile(self.vnfd, cores)
134 self.connection.execute("pkill vPE_vnf")
136 provision_tool(self.connection,
137 os.path.join(self.bin_path, "dpdk_nic_bind.py"))
139 interfaces = self.vnfd["vdu"][0]['external-interface']
141 next((0 for v in interfaces
142 if v['virtual-interface']["vpci"][5] == "0"), 1)
144 bound_pci = [v['virtual-interface']["vpci"] for v in interfaces]
145 for vpci in bound_pci:
146 self.connection.execute(
147 "%s --force -b igb_uio %s" % (dpdk_nic_bind, vpci))
149 QueueFileWrapper(self.q_in, self.q_out, "pipeline>")
150 self._vnf_process = multiprocessing.Process(target=self._run_vpe,
153 self._vnf_process.start()
155 time.sleep(WAIT_TIME) # Give some time for config to load
158 while self.q_out.qsize() > 0:
159 buf.append(self.q_out.get())
160 message = ''.join(buf)
161 if "pipeline>" in message:
162 LOG.info("VPE VNF is up and running.")
163 queue_wrapper.clear()
164 self._resource_collect_start()
165 return self._vnf_process.exitcode
166 if "PANIC" in message:
167 raise RuntimeError("Error starting vPE VNF.")
169 LOG.info("Waiting for VNF to start.. ")
171 if not self._vnf_process.is_alive():
172 raise RuntimeError("vPE VNF process died.")
174 def _get_ports_gateway(self, name):
175 if 'routing_table' in self.vnfd['vdu'][0]:
176 routing_table = self.vnfd['vdu'][0]['routing_table']
178 for route in routing_table:
179 if name == route['if']:
180 return route['gateway']
183 self.execute_command("quit")
184 if self._vnf_process:
185 self._vnf_process.terminate()
187 def _run_vpe(self, filewrapper, vnf_cfg):
188 mgmt_interface = self.vnfd["mgmt-interface"]
190 self.connection = ssh.SSH.from_node(mgmt_interface)
191 self.connection.wait()
193 interfaces = self.vnfd["vdu"][0]['external-interface']
194 port0_ip = ipaddress.ip_interface(six.text_type(
195 "%s/%s" % (interfaces[0]["virtual-interface"]["local_ip"],
196 interfaces[0]["virtual-interface"]["netmask"])))
197 port1_ip = ipaddress.ip_interface(six.text_type(
198 "%s/%s" % (interfaces[1]["virtual-interface"]["local_ip"],
199 interfaces[1]["virtual-interface"]["netmask"])))
200 dst_port0_ip = ipaddress.ip_interface(
201 u"%s/%s" % (interfaces[0]["virtual-interface"]["dst_ip"],
202 interfaces[0]["virtual-interface"]["netmask"]))
203 dst_port1_ip = ipaddress.ip_interface(
204 u"%s/%s" % (interfaces[1]["virtual-interface"]["dst_ip"],
205 interfaces[1]["virtual-interface"]["netmask"]))
207 vpe_vars = {"port0_local_ip": port0_ip.ip.exploded,
208 "port0_dst_ip": dst_port0_ip.ip.exploded,
209 "port0_local_ip_hex":
210 self._ip_to_hex(port0_ip.ip.exploded),
211 "port0_prefixlen": port0_ip.network.prefixlen,
212 "port0_netmask": port0_ip.network.netmask.exploded,
214 self._ip_to_hex(port0_ip.network.netmask.exploded),
216 interfaces[0]["virtual-interface"]["local_mac"],
218 interfaces[0]["virtual-interface"]["dst_mac"],
220 self._get_ports_gateway(interfaces[0]["name"]),
221 "port0_local_network":
222 port0_ip.network.network_address.exploded,
223 "port0_prefix": port0_ip.network.prefixlen,
224 "port1_local_ip": port1_ip.ip.exploded,
225 "port1_dst_ip": dst_port1_ip.ip.exploded,
226 "port1_local_ip_hex":
227 self._ip_to_hex(port1_ip.ip.exploded),
228 "port1_prefixlen": port1_ip.network.prefixlen,
229 "port1_netmask": port1_ip.network.netmask.exploded,
231 self._ip_to_hex(port1_ip.network.netmask.exploded),
233 interfaces[1]["virtual-interface"]["local_mac"],
235 interfaces[1]["virtual-interface"]["dst_mac"],
237 self._get_ports_gateway(interfaces[1]["name"]),
238 "port1_local_network":
239 port1_ip.network.network_address.exploded,
240 "port1_prefix": port1_ip.network.prefixlen,
241 "port0_local_ip6": self._get_port0localip6(),
242 "port1_local_ip6": self._get_port1localip6(),
243 "port0_prefixlen6": self._get_port0prefixlen6(),
244 "port1_prefixlen6": self._get_port1prefixlen6(),
245 "port0_gateway6": self._get_port0gateway6(),
246 "port1_gateway6": self._get_port1gateway6(),
247 "port0_dst_ip_hex6": self._get_port0localip6(),
248 "port1_dst_ip_hex6": self._get_port1localip6(),
249 "port0_dst_netmask_hex6": self._get_port0prefixlen6(),
250 "port1_dst_netmask_hex6": self._get_port1prefixlen6(),
251 "bin_path": self.bin_path,
252 "socket": self.socket}
254 for cfg in os.listdir(vnf_cfg):
256 with open(os.path.join(vnf_cfg, cfg), 'r') as vpe_cfg:
257 vpe_config = vpe_cfg.read()
259 self._provide_config_file(cfg, vpe_config, vpe_vars)
261 LOG.info("Provision and start the vPE")
262 tool_path = provision_tool(self.connection,
263 os.path.join(self.bin_path, "vPE_vnf"))
264 cmd = VPE_PIPELINE_COMMAND.format(cfg_file="/tmp/vpe_config",
265 script="/tmp/vpe_script",
267 self.connection.run(cmd, stdin=filewrapper, stdout=filewrapper,
268 keep_stdin_open=True, pty=True)
270 def _provide_config_file(self, prefix, template, args):
271 cfg, cfg_content = tempfile.mkstemp()
272 cfg = os.fdopen(cfg, "w+")
273 cfg.write(template.format(**args))
275 cfg_file = "/tmp/%s" % prefix
276 self.connection.put(cfg_content, cfg_file)
279 def execute_command(self, cmd):
280 ''' send cmd to vnf process '''
281 LOG.info("VPE command: %s", cmd)
284 self.q_in.put(cmd + "\r\n")
286 while self.q_out.qsize() > 0:
287 output.append(self.q_out.get())
288 return "".join(output)
290 def collect_kpi(self):
291 result = self.get_stats_vpe()
292 collect_stats = self._collect_resource_kpi()
293 result["collect_stats"] = collect_stats
294 LOG.debug("vPE collet Kpis: %s", result)
297 def get_stats_vpe(self):
298 ''' get vpe statistics '''
299 result = {'pkt_in_up_stream': 0, 'pkt_drop_up_stream': 0,
300 'pkt_in_down_stream': 0, 'pkt_drop_down_stream': 0}
301 up_stat_commands = ['p 5 stats port in 0', 'p 5 stats port out 0',
302 'p 5 stats port out 1']
303 down_stat_commands = ['p 9 stats port in 0', 'p 9 stats port out 0']
305 "Pkts in:\\s(\\d+)\\r\\n\\tPkts dropped by " \
306 "AH:\\s(\\d+)\\r\\n\\tPkts dropped by other:\\s(\\d+)"
308 for cmd in up_stat_commands:
309 stats = self.execute_command(cmd)
310 match = re.search(pattern, stats, re.MULTILINE)
312 result["pkt_in_up_stream"] = \
313 result.get("pkt_in_up_stream", 0) + int(match.group(1))
314 result["pkt_drop_up_stream"] = \
315 result.get("pkt_drop_up_stream", 0) + \
316 int(match.group(2)) + int(match.group(3))
318 for cmd in down_stat_commands:
319 stats = self.execute_command(cmd)
320 match = re.search(pattern, stats, re.MULTILINE)
322 result["pkt_in_down_stream"] = \
323 result.get("pkt_in_down_stream", 0) + int(match.group(1))
324 result["pkt_drop_down_stream"] = \
325 result.get("pkt_drop_down_stream", 0) + \
326 int(match.group(2)) + int(match.group(3))