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