Merge "heat: close file before parsing template"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / tg_ping.py
1 # Copyright (c) 2016-2017 Intel Corporation
2 #
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
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
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 """ PING acts as traffic generation and vnf definitions based on IETS Spec """
15
16 from __future__ import absolute_import
17 from __future__ import print_function
18 import logging
19 import multiprocessing
20 import re
21 import time
22 import os
23
24 from yardstick import ssh
25 from yardstick.network_services.vnf_generic.vnf.base import GenericTrafficGen
26 from yardstick.network_services.utils import provision_tool
27
28 LOG = logging.getLogger(__name__)
29
30
31 class PingParser(object):
32     """ Class providing file-like API for talking with SSH connection """
33
34     def __init__(self, q_out):
35         self.queue = q_out
36         self.closed = False
37
38     def write(self, chunk):
39         """ 64 bytes from 10.102.22.93: icmp_seq=1 ttl=64 time=0.296 ms """
40         match = re.search(r"icmp_seq=(\d+).*time=([0-9.]+)", chunk)
41         LOG.debug("Parser called on %s", chunk)
42         if match:
43             # IMPORTANT: in order for the data to be properly taken
44             # in by InfluxDB, it needs to be converted to numeric types
45             self.queue.put({"packets_received": float(match.group(1)),
46                             "rtt": float(match.group(2))})
47
48     def close(self):
49         ''' close the ssh connection '''
50         pass
51
52     def clear(self):
53         ''' clear queue till Empty '''
54         while self.queue.qsize() > 0:
55             self.queue.get()
56
57
58 class PingTrafficGen(GenericTrafficGen):
59     """
60     This traffic generator can ping a single IP with pingsize
61     and target given in traffic profile
62     """
63
64     def __init__(self, vnfd):
65         super(PingTrafficGen, self).__init__(vnfd)
66         self._result = {}
67         self._parser = None
68         self._queue = None
69         self._traffic_process = None
70
71         mgmt_interface = vnfd["mgmt-interface"]
72         ssh_port = mgmt_interface.get("ssh_port", ssh.DEFAULT_PORT)
73         LOG.debug("Connecting to %s", mgmt_interface["ip"])
74
75         self.connection = ssh.SSH(mgmt_interface["user"], mgmt_interface["ip"],
76                                   password=mgmt_interface["password"],
77                                   port=ssh_port)
78         self.connection.wait()
79
80     def _bind_device_kernel(self, connection):
81         dpdk_nic_bind = \
82             provision_tool(self.connection,
83                            os.path.join(self.bin_path, "dpdk_nic_bind.py"))
84
85         drivers = {intf["virtual-interface"]["vpci"]:
86                    intf["virtual-interface"]["driver"]
87                    for intf in self.vnfd["vdu"][0]["external-interface"]}
88
89         commands = \
90             ['"{0}" --force -b "{1}" "{2}"'.format(dpdk_nic_bind, value, key)
91              for key, value in drivers.items()]
92         for command in commands:
93             connection.execute(command)
94
95         for index, out in enumerate(self.vnfd["vdu"][0]["external-interface"]):
96             vpci = out["virtual-interface"]["vpci"]
97             net = "find /sys/class/net -lname '*{}*' -printf '%f'".format(vpci)
98             out = connection.execute(net)[1]
99             ifname = out.split('/')[-1].strip('\n')
100             self.vnfd["vdu"][0]["external-interface"][index][
101                 "virtual-interface"]["local_iface_name"] = ifname
102
103     def scale(self, flavor=""):
104         ''' scale vnfbased on flavor input '''
105         super(PingTrafficGen, self).scale(flavor)
106
107     def instantiate(self, scenario_cfg, context_cfg):
108         self._result = {"packets_received": 0, "rtt": 0}
109         self._bind_device_kernel(self.connection)
110
111     def run_traffic(self, traffic_profile):
112         self._queue = multiprocessing.Queue()
113         self._parser = PingParser(self._queue)
114         self._traffic_process = \
115             multiprocessing.Process(target=self._traffic_runner,
116                                     args=(traffic_profile, self._parser))
117         self._traffic_process.start()
118         # Wait for traffic process to start
119         time.sleep(4)
120         return self._traffic_process.is_alive()
121
122     def listen_traffic(self, traffic_profile):
123         """ Not needed for ping
124
125         :param traffic_profile:
126         :return:
127         """
128         pass
129
130     def _traffic_runner(self, traffic_profile, filewrapper):
131
132         mgmt_interface = self.vnfd["mgmt-interface"]
133         ssh_port = mgmt_interface.get("ssh_port", ssh.DEFAULT_PORT)
134         self.connection = ssh.SSH(mgmt_interface["user"], mgmt_interface["ip"],
135                                   password=mgmt_interface["password"],
136                                   port=ssh_port)
137         self.connection.wait()
138         external_interface = self.vnfd["vdu"][0]["external-interface"]
139         virtual_interface = external_interface[0]["virtual-interface"]
140         target_ip = virtual_interface["dst_ip"].split('/')[0]
141         local_ip = virtual_interface["local_ip"].split('/')[0]
142         local_if_name = \
143             virtual_interface["local_iface_name"].split('/')[0]
144         packet_size = traffic_profile.params["traffic_profile"]["frame_size"]
145
146         run_cmd = []
147
148         run_cmd.append("ip addr flush %s" % local_if_name)
149         run_cmd.append("ip addr add %s/24 dev %s" % (local_ip, local_if_name))
150         run_cmd.append("ip link set %s up" % local_if_name)
151
152         for cmd in run_cmd:
153             self.connection.execute(cmd)
154
155         ping_cmd = ("ping -s %s %s" % (packet_size, target_ip))
156         self.connection.run(ping_cmd, stdout=filewrapper,
157                             keep_stdin_open=True, pty=True)
158
159     def collect_kpi(self):
160         if not self._queue.empty():
161             kpi = self._queue.get()
162             self._result.update(kpi)
163         return self._result
164
165     def terminate(self):
166         if self._traffic_process is not None:
167             self._traffic_process.terminate()