Merge "Open storperf testcase to huawei-pod2"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / base.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 """ Base class implementation for generic vnf implementation """
15
16 from __future__ import absolute_import
17 import logging
18 import ipaddress
19 import six
20
21 from yardstick.network_services.utils import get_nsb_option
22
23 LOG = logging.getLogger(__name__)
24
25
26 class QueueFileWrapper(object):
27     """ Class providing file-like API for talking with SSH connection """
28
29     def __init__(self, q_in, q_out, prompt):
30         self.q_in = q_in
31         self.q_out = q_out
32         self.closed = False
33         self.buf = []
34         self.bufsize = 20
35         self.prompt = prompt
36
37     def read(self, size):
38         """ read chunk from input queue """
39         if self.q_in.qsize() > 0 and size:
40             in_data = self.q_in.get()
41             return in_data
42
43     def write(self, chunk):
44         """ write chunk to output queue """
45         self.buf.append(chunk)
46         # flush on prompt or if we exceed bufsize
47
48         size = sum(len(c) for c in self.buf)
49         if self.prompt in chunk or size > self.bufsize:
50             out = ''.join(self.buf)
51             self.buf = []
52             self.q_out.put(out)
53
54     def close(self):
55         """ close multiprocessing queue """
56         pass
57
58     def clear(self):
59         """ clear queue """
60         while self.q_out.qsize() > 0:
61             self.q_out.get()
62
63
64 class GenericVNF(object):
65     """ Class providing file-like API for generic VNF implementation """
66     def __init__(self, vnfd):
67         super(GenericVNF, self).__init__()
68         self.vnfd = vnfd  # fixme: parse this into a structure
69         # List of statistics we can obtain from this VNF
70         # - ETSI MANO 6.3.1.1 monitoring_parameter
71         self.kpi = self._get_kpi_definition(vnfd)
72         # Standard dictionary containing params like thread no, buffer size etc
73         self.config = {}
74         self.runs_traffic = False
75         self.name = "vnf__1"  # name in topology file
76         self.bin_path = get_nsb_option("bin_path", "")
77
78     @classmethod
79     def _get_kpi_definition(cls, vnfd):
80         """ Get list of KPIs defined in VNFD
81
82         :param vnfd:
83         :return: list of KPIs, e.g. ['throughput', 'latency']
84         """
85         return vnfd['benchmark']['kpi']
86
87     @classmethod
88     def get_ip_version(cls, ip_addr):
89         """ get ip address version v6 or v4 """
90         try:
91             address = ipaddress.ip_address(six.text_type(ip_addr))
92         except ValueError:
93             LOG.error(ip_addr, " is not valid")
94             return
95         else:
96             return address.version
97
98     def _ip_to_hex(self, ip_addr):
99         ip_x = ip_addr
100         if self.get_ip_version(ip_addr) == 4:
101             ip_to_convert = ip_addr.split(".")
102             ip_octect = [int(octect) for octect in ip_to_convert]
103             ip_x = "{0[0]:02X}{0[1]:02X}{0[2]:02X}{0[3]:02X}".format(ip_octect)
104         return ip_x
105
106     def _get_dpdk_port_num(self, name):
107         for intf in self.vnfd['vdu'][0]['external-interface']:
108             if name == intf['name']:
109                 return intf['virtual-interface']['dpdk_port_num']
110
111     def _append_routes(self, ip_pipeline_cfg):
112         if 'routing_table' in self.vnfd['vdu'][0]:
113             routing_table = self.vnfd['vdu'][0]['routing_table']
114
115             where = ip_pipeline_cfg.find("arp_route_tbl")
116             link = ip_pipeline_cfg[:where]
117             route_add = ip_pipeline_cfg[where:]
118
119             tmp = route_add.find('\n')
120             route_add = route_add[tmp:]
121
122             cmds = "arp_route_tbl ="
123
124             for route in routing_table:
125                 net = self._ip_to_hex(route['network'])
126                 net_nm = self._ip_to_hex(route['netmask'])
127                 net_gw = self._ip_to_hex(route['gateway'])
128                 port = self._get_dpdk_port_num(route['if'])
129                 cmd = \
130                     " ({port0_local_ip_hex},{port0_netmask_hex},{dpdk_port},"\
131                     "{port1_local_ip_hex})".format(port0_local_ip_hex=net,
132                                                    port0_netmask_hex=net_nm,
133                                                    dpdk_port=port,
134                                                    port1_local_ip_hex=net_gw)
135                 cmds += cmd
136
137             cmds += '\n'
138             ip_pipeline_cfg = link + cmds + route_add
139
140         return ip_pipeline_cfg
141
142     def _append_nd_routes(self, ip_pipeline_cfg):
143         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
144             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
145
146             where = ip_pipeline_cfg.find("nd_route_tbl")
147             link = ip_pipeline_cfg[:where]
148             route_nd = ip_pipeline_cfg[where:]
149
150             tmp = route_nd.find('\n')
151             route_nd = route_nd[tmp:]
152
153             cmds = "nd_route_tbl ="
154
155             for route in routing_table:
156                 net = route['network']
157                 net_nm = route['netmask']
158                 net_gw = route['gateway']
159                 port = self._get_dpdk_port_num(route['if'])
160                 cmd = \
161                     " ({port0_local_ip_hex},{port0_netmask_hex},{dpdk_port},"\
162                     "{port1_local_ip_hex})".format(port0_local_ip_hex=net,
163                                                    port0_netmask_hex=net_nm,
164                                                    dpdk_port=port,
165                                                    port1_local_ip_hex=net_gw)
166                 cmds += cmd
167
168             cmds += '\n'
169             ip_pipeline_cfg = link + cmds + route_nd
170
171         return ip_pipeline_cfg
172
173     def _get_port0localip6(self):
174         return_value = ""
175         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
176             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
177
178             inc = 0
179             for route in routing_table:
180                 inc += 1
181                 if inc == 1:
182                     return_value = route['network']
183         LOG.info("_get_port0localip6 : %s", return_value)
184         return return_value
185
186     def _get_port1localip6(self):
187         return_value = ""
188         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
189             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
190
191             inc = 0
192             for route in routing_table:
193                 inc += 1
194                 if inc == 2:
195                     return_value = route['network']
196         LOG.info("_get_port1localip6 : %s", return_value)
197         return return_value
198
199     def _get_port0prefixlen6(self):
200         return_value = ""
201         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
202             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
203
204             inc = 0
205             for route in routing_table:
206                 inc += 1
207                 if inc == 1:
208                     return_value = route['netmask']
209         LOG.info("_get_port0prefixlen6 : %s", return_value)
210         return return_value
211
212     def _get_port1prefixlen6(self):
213         return_value = ""
214         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
215             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
216
217             inc = 0
218             for route in routing_table:
219                 inc += 1
220                 if inc == 2:
221                     return_value = route['netmask']
222         LOG.info("_get_port1prefixlen6 : %s", return_value)
223         return return_value
224
225     def _get_port0gateway6(self):
226         return_value = ""
227         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
228             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
229
230             inc = 0
231             for route in routing_table:
232                 inc += 1
233                 if inc == 1:
234                     return_value = route['network']
235         LOG.info("_get_port0gateway6 : %s", return_value)
236         return return_value
237
238     def _get_port1gateway6(self):
239         return_value = ""
240         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
241             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
242
243             inc = 0
244             for route in routing_table:
245                 inc += 1
246                 if inc == 2:
247                     return_value = route['network']
248         LOG.info("_get_port1gateway6 : %s", return_value)
249         return return_value
250
251     def instantiate(self, scenario_cfg, context_cfg):
252         """ Prepare VNF for operation and start the VNF process/VM
253
254         :param scenario_cfg:
255         :param context_cfg:
256         :return: True/False
257         """
258         raise NotImplementedError()
259
260     def terminate(self):
261         """ Kill all VNF processes
262
263         :return:
264         """
265         raise NotImplementedError()
266
267     def scale(self, flavor=""):
268         """
269
270         :param flavor:
271         :return:
272         """
273         raise NotImplementedError()
274
275     def collect_kpi(self):
276         """This method should return a dictionary containing the
277         selected KPI at a given point of time.
278
279         :return: {"kpi": value, "kpi2": value}
280         """
281         raise NotImplementedError()
282
283
284 class GenericTrafficGen(GenericVNF):
285     """ Class providing file-like API for generic traffic generator """
286
287     def __init__(self, vnfd):
288         super(GenericTrafficGen, self).__init__(vnfd)
289         self.runs_traffic = True
290         self.traffic_finished = False
291         self.name = "tgen__1"  # name in topology file
292
293     def run_traffic(self, traffic_profile):
294         """ Generate traffic on the wire according to the given params.
295         Method is non-blocking, returns immediately when traffic process
296         is running. Mandatory.
297
298         :param traffic_profile:
299         :return: True/False
300         """
301         raise NotImplementedError()
302
303     def listen_traffic(self, traffic_profile):
304         """ Listen to traffic with the given parameters.
305         Method is non-blocking, returns immediately when traffic process
306         is running. Optional.
307
308         :param traffic_profile:
309         :return: True/False
310         """
311         pass
312
313     def verify_traffic(self, traffic_profile):
314         """ Verify captured traffic after it has ended. Optional.
315
316         :param traffic_profile:
317         :return: dict
318         """
319         pass
320
321     def terminate(self):
322         """ After this method finishes, all traffic processes should stop. Mandatory.
323
324         :return: True/False
325         """
326         raise NotImplementedError()