Merge "Add vIPSEC VNF for running Crypto performance test case"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / base.py
1 # Copyright (c) 2016-2019 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
15 import abc
16
17 import logging
18 import six
19
20 from yardstick.network_services.helpers.samplevnf_helper import PortPairs
21
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 VnfdHelper(dict):
65
66     def __init__(self, *args, **kwargs):
67         super(VnfdHelper, self).__init__(*args, **kwargs)
68         self.port_pairs = PortPairs(self['vdu'][0]['external-interface'])
69         # port num is not present until binding so we have to memoize
70         self._port_num_map = {}
71
72     @property
73     def mgmt_interface(self):
74         return self["mgmt-interface"]
75
76     @property
77     def vdu(self):
78         return self['vdu']
79
80     @property
81     def vdu0(self):
82         return self.vdu[0]
83
84     @property
85     def interfaces(self):
86         return self.vdu0['external-interface']
87
88     @property
89     def kpi(self):
90         return self['benchmark']['kpi']
91
92     def find_virtual_interface(self, **kwargs):
93         key, value = next(iter(kwargs.items()))
94         for interface in self.interfaces:
95             virtual_intf = interface["virtual-interface"]
96             if virtual_intf[key] == value:
97                 return virtual_intf
98         raise KeyError()
99
100     def find_interface(self, **kwargs):
101         key, value = next(iter(kwargs.items()))
102         for interface in self.interfaces:
103             if interface[key] == value:
104                 return interface
105         raise KeyError()
106
107     # hide dpdk_port_num key so we can abstract
108     def find_interface_by_port(self, port):
109         for interface in self.interfaces:
110             virtual_intf = interface["virtual-interface"]
111             # we have to convert to int to compare
112             if int(virtual_intf['dpdk_port_num']) == port:
113                 return interface
114         raise KeyError()
115
116     def port_num(self, port):
117         # we need interface name -> DPDK port num (PMD ID) -> LINK ID
118         # LINK ID -> PMD ID is governed by the port mask
119         """
120
121         :rtype: int
122         :type port: str
123         """
124         if isinstance(port, dict):
125             intf = port
126         else:
127             intf = self.find_interface(name=port)
128         return self._port_num_map.setdefault(intf["name"],
129                                              int(intf["virtual-interface"]["dpdk_port_num"]))
130
131     def port_nums(self, intfs):
132         return [self.port_num(i) for i in intfs]
133
134     def ports_iter(self):
135         for port_name in self.port_pairs.all_ports:
136             port_num = self.port_num(port_name)
137             yield port_name, port_num
138
139
140 @six.add_metaclass(abc.ABCMeta)
141 class GenericVNF(object):
142     """Class providing file-like API for generic VNF implementation
143
144     Currently the only class implementing this interface is
145     yardstick/network_services/vnf_generic/vnf/sample_vnf:SampleVNF.
146     """
147
148     # centralize network naming convention
149     UPLINK = PortPairs.UPLINK
150     DOWNLINK = PortPairs.DOWNLINK
151
152     def __init__(self, name, vnfd):
153         self.name = name
154         self.vnfd_helper = VnfdHelper(vnfd)
155         # List of statistics we can obtain from this VNF
156         # - ETSI MANO 6.3.1.1 monitoring_parameter
157         self.kpi = self.vnfd_helper.kpi
158         # Standard dictionary containing params like thread no, buffer size etc
159         self.config = {}
160         self.runs_traffic = False
161
162     @abc.abstractmethod
163     def instantiate(self, scenario_cfg, context_cfg):
164         """Prepare VNF for operation and start the VNF process/VM
165
166         :param scenario_cfg: Scenario config
167         :param context_cfg: Context config
168         :return: True/False
169         """
170
171     @abc.abstractmethod
172     def wait_for_instantiate(self):
173         """Wait for VNF to start
174
175         :return: True/False
176         """
177
178     @abc.abstractmethod
179     def terminate(self):
180         """Kill all VNF processes"""
181
182     @abc.abstractmethod
183     def scale(self, flavor=""):
184         """rest
185
186         :param flavor: Name of the flavor.
187         :return:
188         """
189
190     @abc.abstractmethod
191     def collect_kpi(self):
192         """Return a dict containing the selected KPI at a given point of time
193
194         :return: {"kpi": value, "kpi2": value}
195         """
196
197     @abc.abstractmethod
198     def start_collect(self):
199         """Start KPI collection
200         :return: None
201         """
202
203     @abc.abstractmethod
204     def stop_collect(self):
205         """Stop KPI collection
206         :return: None
207         """
208
209
210 @six.add_metaclass(abc.ABCMeta)
211 class GenericTrafficGen(GenericVNF):
212     """Class providing file-like API for generic traffic generator"""
213
214     def __init__(self, name, vnfd):
215         super(GenericTrafficGen, self).__init__(name, vnfd)
216         self.runs_traffic = True
217         self.traffic_finished = False
218
219     @abc.abstractmethod
220     def run_traffic(self, traffic_profile):
221         """Generate traffic on the wire according to the given params.
222
223         This method is non-blocking, returns immediately when traffic process
224         is running. Mandatory.
225
226         :param traffic_profile:
227         :return: True/False
228         """
229
230     @abc.abstractmethod
231     def terminate(self):
232         """After this method finishes, all traffic processes should stop.
233
234         Mandatory.
235
236         :return: True/False
237         """
238
239     def listen_traffic(self, traffic_profile):
240         """Listen to traffic with the given parameters.
241
242         Method is non-blocking, returns immediately when traffic process
243         is running. Optional.
244
245         :param traffic_profile:
246         :return: True/False
247         """
248         pass
249
250     def verify_traffic(self, traffic_profile):
251         """Verify captured traffic after it has ended.
252
253         Optional.
254
255         :param traffic_profile:
256         :return: dict
257         """
258         pass
259
260     def wait_for_instantiate(self):
261         """Wait for an instance to load.
262
263         Optional.
264
265         :return: True/False
266         """
267         pass
268
269     def start_collect(self):
270         """Start KPI collection.
271
272         Traffic measurements are always collected during injection.
273
274         Optional.
275
276         :return: True/False
277         """
278         pass
279
280     def stop_collect(self):
281         """Stop KPI collection.
282
283         Optional.
284
285         :return: True/False
286         """
287         pass