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