Merge "Get node IPs and IDs according to env"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / vpe_vnf.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 """ vPE (Power Edge router) VNF model definitions based on IETS Spec """
15
16 from __future__ import absolute_import
17 from __future__ import print_function
18
19
20 import os
21 import logging
22 import re
23 import posixpath
24
25 from six.moves import configparser, zip
26
27 from yardstick.network_services.helpers.samplevnf_helper import PortPairs
28 from yardstick.network_services.pipeline import PipelineRules
29 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, DpdkVnfSetupEnvHelper
30
31 LOG = logging.getLogger(__name__)
32
33 VPE_PIPELINE_COMMAND = """sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script}"""
34
35 VPE_COLLECT_KPI = """\
36 Pkts in:\s(\d+)\r\n\
37 \tPkts dropped by AH:\s(\d+)\r\n\
38 \tPkts dropped by other:\s(\d+)\
39 """
40
41
42 class ConfigCreate(object):
43
44     @staticmethod
45     def vpe_tmq(config, index):
46         tm_q = 'TM{0}'.format(index)
47         config.add_section(tm_q)
48         config.set(tm_q, 'burst_read', '24')
49         config.set(tm_q, 'burst_write', '32')
50         config.set(tm_q, 'cfg', '/tmp/full_tm_profile_10G.cfg')
51         return config
52
53     def __init__(self, vnfd_helper, socket):
54         super(ConfigCreate, self).__init__()
55         self.sw_q = -1
56         self.sink_q = -1
57         self.n_pipeline = 1
58         self.vnfd_helper = vnfd_helper
59         self.uplink_ports = self.vnfd_helper.port_pairs.uplink_ports
60         self.downlink_ports = self.vnfd_helper.port_pairs.downlink_ports
61         self.pipeline_per_port = 9
62         self.socket = socket
63
64     def vpe_initialize(self, config):
65         config.add_section('EAL')
66         config.set('EAL', 'log_level', '0')
67
68         config.add_section('PIPELINE0')
69         config.set('PIPELINE0', 'type', 'MASTER')
70         config.set('PIPELINE0', 'core', 's%sC0' % self.socket)
71
72         config.add_section('MEMPOOL0')
73         config.set('MEMPOOL0', 'pool_size', '256K')
74
75         config.add_section('MEMPOOL1')
76         config.set('MEMPOOL1', 'pool_size', '2M')
77         return config
78
79     def vpe_rxq(self, config):
80         for port in self.downlink_ports:
81             new_section = 'RXQ{0}.0'.format(self.vnfd_helper.port_num(port))
82             config.add_section(new_section)
83             config.set(new_section, 'mempool', 'MEMPOOL1')
84
85         return config
86
87     def get_sink_swq(self, parser, pipeline, k, index):
88         sink = ""
89         pktq = parser.get(pipeline, k)
90         if "SINK" in pktq:
91             self.sink_q += 1
92             sink = " SINK{0}".format(self.sink_q)
93         if "TM" in pktq:
94             sink = " TM{0}".format(index)
95         pktq = "SWQ{0}{1}".format(self.sw_q, sink)
96         return pktq
97
98     def vpe_upstream(self, vnf_cfg, index=0):
99         parser = configparser.ConfigParser()
100         parser.read(os.path.join(vnf_cfg, 'vpe_upstream'))
101
102         for pipeline in parser.sections():
103             for k, v in parser.items(pipeline):
104                 if k == "pktq_in":
105                     if "RXQ" in v:
106                         port = self.vnfd_helper.port_num(self.uplink_ports[index])
107                         value = "RXQ{0}.0".format(port)
108                     else:
109                         value = self.get_sink_swq(parser, pipeline, k, index)
110
111                     parser.set(pipeline, k, value)
112
113                 elif k == "pktq_out":
114                     if "TXQ" in v:
115                         port = self.vnfd_helper.port_num(self.downlink_ports[index])
116                         value = "TXQ{0}.0".format(port)
117                     else:
118                         self.sw_q += 1
119                         value = self.get_sink_swq(parser, pipeline, k, index)
120
121                     parser.set(pipeline, k, value)
122
123             new_pipeline = 'PIPELINE{0}'.format(self.n_pipeline)
124             if new_pipeline != pipeline:
125                 parser._sections[new_pipeline] = parser._sections[pipeline]
126                 parser._sections.pop(pipeline)
127             self.n_pipeline += 1
128         return parser
129
130     def vpe_downstream(self, vnf_cfg, index):
131         parser = configparser.ConfigParser()
132         parser.read(os.path.join(vnf_cfg, 'vpe_downstream'))
133         for pipeline in parser.sections():
134             for k, v in parser.items(pipeline):
135
136                 if k == "pktq_in":
137                     port = self.vnfd_helper.port_num(self.downlink_ports[index])
138                     if "RXQ" not in v:
139                         value = self.get_sink_swq(parser, pipeline, k, index)
140                     elif "TM" in v:
141                         value = "RXQ{0}.0 TM{1}".format(port, index)
142                     else:
143                         value = "RXQ{0}.0".format(port)
144
145                     parser.set(pipeline, k, value)
146
147                 if k == "pktq_out":
148                     port = self.vnfd_helper.port_num(self.uplink_ports[index])
149                     if "TXQ" not in v:
150                         self.sw_q += 1
151                         value = self.get_sink_swq(parser, pipeline, k, index)
152                     elif "TM" in v:
153                         value = "TXQ{0}.0 TM{1}".format(port, index)
154                     else:
155                         value = "TXQ{0}.0".format(port)
156
157                     parser.set(pipeline, k, value)
158
159             new_pipeline = 'PIPELINE{0}'.format(self.n_pipeline)
160             if new_pipeline != pipeline:
161                 parser._sections[new_pipeline] = parser._sections[pipeline]
162                 parser._sections.pop(pipeline)
163             self.n_pipeline += 1
164         return parser
165
166     def create_vpe_config(self, vnf_cfg):
167         config = configparser.ConfigParser()
168         vpe_cfg = os.path.join("/tmp/vpe_config")
169         with open(vpe_cfg, 'w') as cfg_file:
170             config = self.vpe_initialize(config)
171             config = self.vpe_rxq(config)
172             config.write(cfg_file)
173             for index in range(0, len(self.uplink_ports)):
174                 config = self.vpe_upstream(vnf_cfg, index)
175                 config.write(cfg_file)
176                 config = self.vpe_downstream(vnf_cfg, index)
177                 config = self.vpe_tmq(config, index)
178                 config.write(cfg_file)
179
180     def generate_vpe_script(self, interfaces):
181         rules = PipelineRules(pipeline_id=1)
182         for uplink_port, downlink_port in zip(self.uplink_ports, self.downlink_ports):
183
184             uplink_intf = \
185                 next(intf["virtual-interface"] for intf in interfaces
186                      if intf["name"] == uplink_port)
187             downlink_intf = \
188                 next(intf["virtual-interface"] for intf in interfaces
189                      if intf["name"] == downlink_port)
190
191             dst_port0_ip = uplink_intf["dst_ip"]
192             dst_port1_ip = downlink_intf["dst_ip"]
193             dst_port0_mac = uplink_intf["dst_mac"]
194             dst_port1_mac = downlink_intf["dst_mac"]
195
196             rules.add_firewall_script(dst_port0_ip)
197             rules.next_pipeline()
198             rules.add_flow_classification_script()
199             rules.next_pipeline()
200             rules.add_flow_action()
201             rules.next_pipeline()
202             rules.add_flow_action2()
203             rules.next_pipeline()
204             rules.add_route_script(dst_port1_ip, dst_port1_mac)
205             rules.next_pipeline()
206             rules.add_route_script2(dst_port0_ip, dst_port0_mac)
207             rules.next_pipeline(num=4)
208
209         return rules.get_string()
210
211     def generate_tm_cfg(self, vnf_cfg, index=0):
212         vnf_cfg = os.path.join(vnf_cfg, "full_tm_profile_10G.cfg")
213         if os.path.exists(vnf_cfg):
214             return open(vnf_cfg).read()
215
216
217 class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper):
218
219     APP_NAME = 'vPE_vnf'
220     CFG_CONFIG = "/tmp/vpe_config"
221     CFG_SCRIPT = "/tmp/vpe_script"
222     TM_CONFIG = "/tmp/full_tm_profile_10G.cfg"
223     CORES = ['0', '1', '2', '3', '4', '5']
224     PIPELINE_COMMAND = VPE_PIPELINE_COMMAND
225
226     def _build_vnf_ports(self):
227         self._port_pairs = PortPairs(self.vnfd_helper.interfaces)
228         self.uplink_ports = self._port_pairs.uplink_ports
229         self.downlink_ports = self._port_pairs.downlink_ports
230         self.all_ports = self._port_pairs.all_ports
231
232     def build_config(self):
233         vpe_vars = {
234             "bin_path": self.ssh_helper.bin_path,
235             "socket": self.socket,
236         }
237
238         self._build_vnf_ports()
239         vpe_conf = ConfigCreate(self.vnfd_helper, self.socket)
240         vpe_conf.create_vpe_config(self.scenario_helper.vnf_cfg)
241
242         config_basename = posixpath.basename(self.CFG_CONFIG)
243         script_basename = posixpath.basename(self.CFG_SCRIPT)
244         tm_basename = posixpath.basename(self.TM_CONFIG)
245         with open(self.CFG_CONFIG) as handle:
246             vpe_config = handle.read()
247
248         self.ssh_helper.upload_config_file(config_basename, vpe_config.format(**vpe_vars))
249
250         vpe_script = vpe_conf.generate_vpe_script(self.vnfd_helper.interfaces)
251         self.ssh_helper.upload_config_file(script_basename, vpe_script.format(**vpe_vars))
252
253         tm_config = vpe_conf.generate_tm_cfg(self.scenario_helper.vnf_cfg)
254         self.ssh_helper.upload_config_file(tm_basename, tm_config)
255
256         LOG.info("Provision and start the %s", self.APP_NAME)
257         LOG.info(self.CFG_CONFIG)
258         LOG.info(self.CFG_SCRIPT)
259         self._build_pipeline_kwargs()
260         return self.PIPELINE_COMMAND.format(**self.pipeline_kwargs)
261
262
263 class VpeApproxVnf(SampleVNF):
264     """ This class handles vPE VNF model-driver definitions """
265
266     APP_NAME = 'vPE_vnf'
267     APP_WORD = 'vpe'
268     COLLECT_KPI = VPE_COLLECT_KPI
269     WAIT_TIME = 20
270
271     def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
272         if setup_env_helper_type is None:
273             setup_env_helper_type = VpeApproxSetupEnvHelper
274
275         super(VpeApproxVnf, self).__init__(name, vnfd, setup_env_helper_type, resource_helper_type)
276
277     def get_stats(self, *args, **kwargs):
278         raise NotImplementedError
279
280     def collect_kpi(self):
281         result = {
282             'pkt_in_up_stream': 0,
283             'pkt_drop_up_stream': 0,
284             'pkt_in_down_stream': 0,
285             'pkt_drop_down_stream': 0,
286             'collect_stats': self.resource_helper.collect_kpi(),
287         }
288
289         indexes_in = [1]
290         indexes_drop = [2, 3]
291         command = 'p {0} stats port {1} 0'
292         for index, direction in ((5, 'up'), (9, 'down')):
293             key_in = "pkt_in_{0}_stream".format(direction)
294             key_drop = "pkt_drop_{0}_stream".format(direction)
295             for mode in ('in', 'out'):
296                 stats = self.vnf_execute(command.format(index, mode))
297                 match = re.search(self.COLLECT_KPI, stats, re.MULTILINE)
298                 if not match:
299                     continue
300                 result[key_in] += sum(int(match.group(x)) for x in indexes_in)
301                 result[key_drop] += sum(int(match.group(x)) for x in indexes_drop)
302
303         LOG.debug("%s collect KPIs %s", self.APP_NAME, result)
304         return result