Merge "Use "protocol" parameters in iperf3 yaml and task file"
[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
19 from yardstick.network_services.helpers.samplevnf_helper import PortPairs
20
21 LOG = logging.getLogger(__name__)
22
23
24 class QueueFileWrapper(object):
25     """ Class providing file-like API for talking with SSH connection """
26
27     def __init__(self, q_in, q_out, prompt):
28         self.q_in = q_in
29         self.q_out = q_out
30         self.closed = False
31         self.buf = []
32         self.bufsize = 20
33         self.prompt = prompt
34
35     def read(self, size):
36         """ read chunk from input queue """
37         if self.q_in.qsize() > 0 and size:
38             in_data = self.q_in.get()
39             return in_data
40
41     def write(self, chunk):
42         """ write chunk to output queue """
43         self.buf.append(chunk)
44         # flush on prompt or if we exceed bufsize
45
46         size = sum(len(c) for c in self.buf)
47         if self.prompt in chunk or size > self.bufsize:
48             out = ''.join(self.buf)
49             self.buf = []
50             self.q_out.put(out)
51
52     def close(self):
53         """ close multiprocessing queue """
54         pass
55
56     def clear(self):
57         """ clear queue """
58         while self.q_out.qsize() > 0:
59             self.q_out.get()
60
61
62 class VnfdHelper(dict):
63
64     def __init__(self, *args, **kwargs):
65         super(VnfdHelper, self).__init__(*args, **kwargs)
66         self.port_pairs = PortPairs(self['vdu'][0]['external-interface'])
67         # port num is not present until binding so we have to memoize
68         self._port_num_map = {}
69
70     @property
71     def mgmt_interface(self):
72         return self["mgmt-interface"]
73
74     @property
75     def vdu(self):
76         return self['vdu']
77
78     @property
79     def vdu0(self):
80         return self.vdu[0]
81
82     @property
83     def interfaces(self):
84         return self.vdu0['external-interface']
85
86     @property
87     def kpi(self):
88         return self['benchmark']['kpi']
89
90     def find_virtual_interface(self, **kwargs):
91         key, value = next(iter(kwargs.items()))
92         for interface in self.interfaces:
93             virtual_intf = interface["virtual-interface"]
94             if virtual_intf[key] == value:
95                 return interface
96         raise KeyError()
97
98     def find_interface(self, **kwargs):
99         key, value = next(iter(kwargs.items()))
100         for interface in self.interfaces:
101             if interface[key] == value:
102                 return interface
103         raise KeyError()
104
105     # hide dpdk_port_num key so we can abstract
106     def find_interface_by_port(self, port):
107         for interface in self.interfaces:
108             virtual_intf = interface["virtual-interface"]
109             # we have to convert to int to compare
110             if int(virtual_intf['dpdk_port_num']) == port:
111                 return interface
112         raise KeyError()
113
114     def port_num(self, port):
115         # we need interface name -> DPDK port num (PMD ID) -> LINK ID
116         # LINK ID -> PMD ID is governed by the port mask
117         """
118
119         :rtype: int
120         :type port: str
121         """
122         if isinstance(port, dict):
123             intf = port
124         else:
125             intf = self.find_interface(name=port)
126         return self._port_num_map.setdefault(intf["name"],
127                                              int(intf["virtual-interface"]["dpdk_port_num"]))
128
129     def port_nums(self, intfs):
130         return [self.port_num(i) for i in intfs]
131
132     def ports_iter(self):
133         for port_name in self.port_pairs.all_ports:
134             port_num = self.port_num(port_name)
135             yield port_name, port_num
136
137
138 class VNFObject(object):
139
140     # centralize network naming convention
141     UPLINK = PortPairs.UPLINK
142     DOWNLINK = PortPairs.DOWNLINK
143
144     def __init__(self, name, vnfd):
145         super(VNFObject, self).__init__()
146         self.name = name
147         self.vnfd_helper = VnfdHelper(vnfd)  # fixme: parse this into a structure
148
149
150 class GenericVNF(VNFObject):
151
152     """ Class providing file-like API for generic VNF implementation """
153     def __init__(self, name, vnfd):
154         super(GenericVNF, self).__init__(name, vnfd)
155         # List of statistics we can obtain from this VNF
156         # - ETSI MANO 6.3.1.1 monitoring_parameter
157         self.kpi = self._get_kpi_definition()
158         # Standard dictionary containing params like thread no, buffer size etc
159         self.config = {}
160         self.runs_traffic = False
161
162     def _get_kpi_definition(self):
163         """ Get list of KPIs defined in VNFD
164
165         :param vnfd:
166         :return: list of KPIs, e.g. ['throughput', 'latency']
167         """
168         return self.vnfd_helper.kpi
169
170     def instantiate(self, scenario_cfg, context_cfg):
171         """ Prepare VNF for operation and start the VNF process/VM
172
173         :param scenario_cfg:
174         :param context_cfg:
175         :return: True/False
176         """
177         raise NotImplementedError()
178
179     def wait_for_instantiate(self):
180         """ Wait for VNF to start
181
182         :return: True/False
183         """
184         raise NotImplementedError()
185
186     def terminate(self):
187         """ Kill all VNF processes
188
189         :return:
190         """
191         raise NotImplementedError()
192
193     def scale(self, flavor=""):
194         """
195
196         :param flavor:
197         :return:
198         """
199         raise NotImplementedError()
200
201     def collect_kpi(self):
202         """This method should return a dictionary containing the
203         selected KPI at a given point of time.
204
205         :return: {"kpi": value, "kpi2": value}
206         """
207         raise NotImplementedError()
208
209
210 class GenericTrafficGen(GenericVNF):
211     """ Class providing file-like API for generic traffic generator """
212
213     def __init__(self, name, vnfd):
214         super(GenericTrafficGen, self).__init__(name, vnfd)
215         self.runs_traffic = True
216         self.traffic_finished = False
217
218     def run_traffic(self, traffic_profile):
219         """ Generate traffic on the wire according to the given params.
220         Method is non-blocking, returns immediately when traffic process
221         is running. Mandatory.
222
223         :param traffic_profile:
224         :return: True/False
225         """
226         raise NotImplementedError()
227
228     def listen_traffic(self, traffic_profile):
229         """ Listen to traffic with the given parameters.
230         Method is non-blocking, returns immediately when traffic process
231         is running. Optional.
232
233         :param traffic_profile:
234         :return: True/False
235         """
236         pass
237
238     def verify_traffic(self, traffic_profile):
239         """ Verify captured traffic after it has ended. Optional.
240
241         :param traffic_profile:
242         :return: dict
243         """
244         pass
245
246     def terminate(self):
247         """ After this method finishes, all traffic processes should stop. Mandatory.
248
249         :return: True/False
250         """
251         raise NotImplementedError()