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']
123 mgmt_interface = self.vnfd["mgmt-interface"]
124 ssh_port = mgmt_interface.get("ssh_port", ssh.DEFAULT_PORT)
126 self.connection = ssh.SSH(mgmt_interface["user"], mgmt_interface["ip"],
127 password=mgmt_interface["password"],
130 self.connection.wait()
132 self.setup_vnf_environment(self.connection)
134 cores = self._get_cpu_sibling_list()
135 self.resource = ResourceProfile(self.vnfd, cores)
137 self.connection.execute("pkill vPE_vnf")
139 provision_tool(self.connection,
140 os.path.join(self.bin_path, "dpdk_nic_bind.py"))
142 interfaces = self.vnfd["vdu"][0]['external-interface']
144 next((0 for v in interfaces
145 if v['virtual-interface']["vpci"][5] == "0"), 1)
147 bound_pci = [v['virtual-interface']["vpci"] for v in interfaces]
148 for vpci in bound_pci:
149 self.connection.execute(
150 "%s --force -b igb_uio %s" % (dpdk_nic_bind, vpci))
152 QueueFileWrapper(self.q_in, self.q_out, "pipeline>")
153 self._vnf_process = multiprocessing.Process(target=self._run_vpe,
156 self._vnf_process.start()
158 time.sleep(WAIT_TIME) # Give some time for config to load
161 while self.q_out.qsize() > 0:
162 buf.append(self.q_out.get())
163 message = ''.join(buf)
164 if "pipeline>" in message:
165 LOG.info("VPE VNF is up and running.")
166 queue_wrapper.clear()
167 self._resource_collect_start()
168 return self._vnf_process.exitcode
169 if "PANIC" in message:
170 raise RuntimeError("Error starting vPE VNF.")
172 LOG.info("Waiting for VNF to start.. ")
174 if not self._vnf_process.is_alive():
175 raise RuntimeError("vPE VNF process died.")
177 def _get_ports_gateway(self, name):
178 if 'routing_table' in self.vnfd['vdu'][0]:
179 routing_table = self.vnfd['vdu'][0]['routing_table']
181 for route in routing_table:
182 if name == route['if']:
183 return route['gateway']
186 self.execute_command("quit")
187 if self._vnf_process:
188 self._vnf_process.terminate()
190 def _run_vpe(self, filewrapper, vnf_cfg):
191 mgmt_interface = self.vnfd["mgmt-interface"]
192 ssh_port = mgmt_interface.get("ssh_port", ssh.DEFAULT_PORT)
193 self.connection = ssh.SSH(mgmt_interface["user"], mgmt_interface["ip"],
194 password=mgmt_interface["password"],
196 self.connection.wait()
197 interfaces = self.vnfd["vdu"][0]['external-interface']
198 port0_ip = ipaddress.ip_interface(six.text_type(
199 "%s/%s" % (interfaces[0]["virtual-interface"]["local_ip"],
200 interfaces[0]["virtual-interface"]["netmask"])))
201 port1_ip = ipaddress.ip_interface(six.text_type(
202 "%s/%s" % (interfaces[1]["virtual-interface"]["local_ip"],
203 interfaces[1]["virtual-interface"]["netmask"])))
204 dst_port0_ip = ipaddress.ip_interface(
205 u"%s/%s" % (interfaces[0]["virtual-interface"]["dst_ip"],
206 interfaces[0]["virtual-interface"]["netmask"]))
207 dst_port1_ip = ipaddress.ip_interface(
208 u"%s/%s" % (interfaces[1]["virtual-interface"]["dst_ip"],
209 interfaces[1]["virtual-interface"]["netmask"]))
211 vpe_vars = {"port0_local_ip": port0_ip.ip.exploded,
212 "port0_dst_ip": dst_port0_ip.ip.exploded,
213 "port0_local_ip_hex":
214 self._ip_to_hex(port0_ip.ip.exploded),
215 "port0_prefixlen": port0_ip.network.prefixlen,
216 "port0_netmask": port0_ip.network.netmask.exploded,
218 self._ip_to_hex(port0_ip.network.netmask.exploded),
220 interfaces[0]["virtual-interface"]["local_mac"],
222 interfaces[0]["virtual-interface"]["dst_mac"],
224 self._get_ports_gateway(interfaces[0]["name"]),
225 "port0_local_network":
226 port0_ip.network.network_address.exploded,
227 "port0_prefix": port0_ip.network.prefixlen,
228 "port1_local_ip": port1_ip.ip.exploded,
229 "port1_dst_ip": dst_port1_ip.ip.exploded,
230 "port1_local_ip_hex":
231 self._ip_to_hex(port1_ip.ip.exploded),
232 "port1_prefixlen": port1_ip.network.prefixlen,
233 "port1_netmask": port1_ip.network.netmask.exploded,
235 self._ip_to_hex(port1_ip.network.netmask.exploded),
237 interfaces[1]["virtual-interface"]["local_mac"],
239 interfaces[1]["virtual-interface"]["dst_mac"],
241 self._get_ports_gateway(interfaces[1]["name"]),
242 "port1_local_network":
243 port1_ip.network.network_address.exploded,
244 "port1_prefix": port1_ip.network.prefixlen,
245 "port0_local_ip6": self._get_port0localip6(),
246 "port1_local_ip6": self._get_port1localip6(),
247 "port0_prefixlen6": self._get_port0prefixlen6(),
248 "port1_prefixlen6": self._get_port1prefixlen6(),
249 "port0_gateway6": self._get_port0gateway6(),
250 "port1_gateway6": self._get_port1gateway6(),
251 "port0_dst_ip_hex6": self._get_port0localip6(),
252 "port1_dst_ip_hex6": self._get_port1localip6(),
253 "port0_dst_netmask_hex6": self._get_port0prefixlen6(),
254 "port1_dst_netmask_hex6": self._get_port1prefixlen6(),
255 "bin_path": self.bin_path,
256 "socket": self.socket}
258 for cfg in os.listdir(vnf_cfg):
260 with open(os.path.join(vnf_cfg, cfg), 'r') as vpe_cfg:
261 vpe_config = vpe_cfg.read()
263 self._provide_config_file(cfg, vpe_config, vpe_vars)
265 LOG.info("Provision and start the vPE")
266 tool_path = provision_tool(self.connection,
267 os.path.join(self.bin_path, "vPE_vnf"))
268 cmd = VPE_PIPELINE_COMMAND.format(cfg_file="/tmp/vpe_config",
269 script="/tmp/vpe_script",
271 self.connection.run(cmd, stdin=filewrapper, stdout=filewrapper,
272 keep_stdin_open=True, pty=True)
274 def _provide_config_file(self, prefix, template, args):
275 cfg, cfg_content = tempfile.mkstemp()
276 cfg = os.fdopen(cfg, "w+")
277 cfg.write(template.format(**args))
279 cfg_file = "/tmp/%s" % prefix
280 self.connection.put(cfg_content, cfg_file)
283 def execute_command(self, cmd):
284 ''' send cmd to vnf process '''
285 LOG.info("VPE command: %s", cmd)
288 self.q_in.put(cmd + "\r\n")
290 while self.q_out.qsize() > 0:
291 output.append(self.q_out.get())
292 return "".join(output)
294 def collect_kpi(self):
295 result = self.get_stats_vpe()
296 collect_stats = self._collect_resource_kpi()
297 result["collect_stats"] = collect_stats
298 LOG.debug("vPE collet Kpis: %s", result)
301 def get_stats_vpe(self):
302 ''' get vpe statistics '''
303 result = {'pkt_in_up_stream': 0, 'pkt_drop_up_stream': 0,
304 'pkt_in_down_stream': 0, 'pkt_drop_down_stream': 0}
305 up_stat_commands = ['p 5 stats port in 0', 'p 5 stats port out 0',
306 'p 5 stats port out 1']
307 down_stat_commands = ['p 9 stats port in 0', 'p 9 stats port out 0']
309 "Pkts in:\\s(\\d+)\\r\\n\\tPkts dropped by " \
310 "AH:\\s(\\d+)\\r\\n\\tPkts dropped by other:\\s(\\d+)"
312 for cmd in up_stat_commands:
313 stats = self.execute_command(cmd)
314 match = re.search(pattern, stats, re.MULTILINE)
316 result["pkt_in_up_stream"] = \
317 result.get("pkt_in_up_stream", 0) + int(match.group(1))
318 result["pkt_drop_up_stream"] = \
319 result.get("pkt_drop_up_stream", 0) + \
320 int(match.group(2)) + int(match.group(3))
322 for cmd in down_stat_commands:
323 stats = self.execute_command(cmd)
324 match = re.search(pattern, stats, re.MULTILINE)
326 result["pkt_in_down_stream"] = \
327 result.get("pkt_in_down_stream", 0) + int(match.group(1))
328 result["pkt_drop_down_stream"] = \
329 result.get("pkt_drop_down_stream", 0) + \
330 int(match.group(2)) + int(match.group(3))