Merge "heat: minor fixes, don't use len > 0 and use next instead of [0]"
[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_to_convert = ip_addr.split(".")
100         ip_x = ip_addr
101         if self.get_ip_version(ip_addr) == 4:
102             ip_to_convert = ip_addr.split(".")
103             ip_octect = [int(octect) for octect in ip_to_convert]
104             ip_x = "{0[0]:02X}{0[1]:02X}{0[2]:02X}{0[3]:02X}".format(ip_octect)
105         return ip_x
106
107     def _get_dpdk_port_num(self, name):
108         for intf in self.vnfd['vdu'][0]['external-interface']:
109             if name == intf['name']:
110                 return intf['virtual-interface']['dpdk_port_num']
111
112     def _append_routes(self, ip_pipeline_cfg):
113         if 'routing_table' in self.vnfd['vdu'][0]:
114             routing_table = self.vnfd['vdu'][0]['routing_table']
115
116             where = ip_pipeline_cfg.find("arp_route_tbl")
117             link = ip_pipeline_cfg[:where]
118             route_add = ip_pipeline_cfg[where:]
119
120             tmp = route_add.find('\n')
121             route_add = route_add[tmp:]
122
123             cmds = "arp_route_tbl ="
124
125             for route in routing_table:
126                 net = self._ip_to_hex(route['network'])
127                 net_nm = self._ip_to_hex(route['netmask'])
128                 net_gw = self._ip_to_hex(route['gateway'])
129                 port = self._get_dpdk_port_num(route['if'])
130                 cmd = \
131                     " ({port0_local_ip_hex},{port0_netmask_hex},{dpdk_port},"\
132                     "{port1_local_ip_hex})".format(port0_local_ip_hex=net,
133                                                    port0_netmask_hex=net_nm,
134                                                    dpdk_port=port,
135                                                    port1_local_ip_hex=net_gw)
136                 cmds += cmd
137
138             cmds += '\n'
139             ip_pipeline_cfg = link + cmds + route_add
140
141         return ip_pipeline_cfg
142
143     def _append_nd_routes(self, ip_pipeline_cfg):
144         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
145             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
146
147             where = ip_pipeline_cfg.find("nd_route_tbl")
148             link = ip_pipeline_cfg[:where]
149             route_nd = ip_pipeline_cfg[where:]
150
151             tmp = route_nd.find('\n')
152             route_nd = route_nd[tmp:]
153
154             cmds = "nd_route_tbl ="
155
156             for route in routing_table:
157                 net = route['network']
158                 net_nm = route['netmask']
159                 net_gw = route['gateway']
160                 port = self._get_dpdk_port_num(route['if'])
161                 cmd = \
162                     " ({port0_local_ip_hex},{port0_netmask_hex},{dpdk_port},"\
163                     "{port1_local_ip_hex})".format(port0_local_ip_hex=net,
164                                                    port0_netmask_hex=net_nm,
165                                                    dpdk_port=port,
166                                                    port1_local_ip_hex=net_gw)
167                 cmds += cmd
168
169             cmds += '\n'
170             ip_pipeline_cfg = link + cmds + route_nd
171
172         return ip_pipeline_cfg
173
174     def _get_port0localip6(self):
175         return_value = ""
176         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
177             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
178
179             inc = 0
180             for route in routing_table:
181                 inc += 1
182                 if inc == 1:
183                     return_value = route['network']
184         LOG.info("_get_port0localip6 : %s", return_value)
185         return return_value
186
187     def _get_port1localip6(self):
188         return_value = ""
189         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
190             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
191
192             inc = 0
193             for route in routing_table:
194                 inc += 1
195                 if inc == 2:
196                     return_value = route['network']
197         LOG.info("_get_port1localip6 : %s", return_value)
198         return return_value
199
200     def _get_port0prefixlen6(self):
201         return_value = ""
202         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
203             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
204
205             inc = 0
206             for route in routing_table:
207                 inc += 1
208                 if inc == 1:
209                     return_value = route['netmask']
210         LOG.info("_get_port0prefixlen6 : %s", return_value)
211         return return_value
212
213     def _get_port1prefixlen6(self):
214         return_value = ""
215         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
216             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
217
218             inc = 0
219             for route in routing_table:
220                 inc += 1
221                 if inc == 2:
222                     return_value = route['netmask']
223         LOG.info("_get_port1prefixlen6 : %s", return_value)
224         return return_value
225
226     def _get_port0gateway6(self):
227         return_value = ""
228         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
229             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
230
231             inc = 0
232             for route in routing_table:
233                 inc += 1
234                 if inc == 1:
235                     return_value = route['network']
236         LOG.info("_get_port0gateway6 : %s", return_value)
237         return return_value
238
239     def _get_port1gateway6(self):
240         return_value = ""
241         if 'nd_route_tbl' in self.vnfd['vdu'][0]:
242             routing_table = self.vnfd['vdu'][0]['nd_route_tbl']
243
244             inc = 0
245             for route in routing_table:
246                 inc += 1
247                 if inc == 2:
248                     return_value = route['network']
249         LOG.info("_get_port1gateway6 : %s", return_value)
250         return return_value
251
252     def instantiate(self, scenario_cfg, context_cfg):
253         """ Prepare VNF for operation and start the VNF process/VM
254
255         :param scenario_cfg:
256         :param context_cfg:
257         :return: True/False
258         """
259         raise NotImplementedError()
260
261     def terminate(self):
262         """ Kill all VNF processes
263
264         :return:
265         """
266         raise NotImplementedError()
267
268     def scale(self, flavor=""):
269         """
270
271         :param flavor:
272         :return:
273         """
274         raise NotImplementedError()
275
276     def collect_kpi(self):
277         """This method should return a dictionary containing the
278         selected KPI at a given point of time.
279
280         :return: {"kpi": value, "kpi2": value}
281         """
282         raise NotImplementedError()
283
284
285 class GenericTrafficGen(GenericVNF):
286     """ Class providing file-like API for generic traffic generator """
287
288     def __init__(self, vnfd):
289         super(GenericTrafficGen, self).__init__(vnfd)
290         self.runs_traffic = True
291         self.traffic_finished = False
292         self.name = "tgen__1"  # name in topology file
293
294     def run_traffic(self, traffic_profile):
295         """ Generate traffic on the wire according to the given params.
296         Method is non-blocking, returns immediately when traffic process
297         is running. Mandatory.
298
299         :param traffic_profile:
300         :return: True/False
301         """
302         raise NotImplementedError()
303
304     def listen_traffic(self, traffic_profile):
305         """ Listen to traffic with the given parameters.
306         Method is non-blocking, returns immediately when traffic process
307         is running. Optional.
308
309         :param traffic_profile:
310         :return: True/False
311         """
312         pass
313
314     def verify_traffic(self, traffic_profile):
315         """ Verify captured traffic after it has ended. Optional.
316
317         :param traffic_profile:
318         :return: dict
319         """
320         pass
321
322     def terminate(self):
323         """ After this method finishes, all traffic processes should stop. Mandatory.
324
325         :return: True/False
326         """
327         raise NotImplementedError()