1 # Copyright (c) 2016-2017 Intel Corporation
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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 """
16 from __future__ import absolute_import
17 from __future__ import print_function
25 from six.moves import configparser, zip
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
31 LOG = logging.getLogger(__name__)
33 VPE_PIPELINE_COMMAND = """sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script}"""
35 VPE_COLLECT_KPI = """\
37 \tPkts dropped by AH:\s(\d+)\r\n\
38 \tPkts dropped by other:\s(\d+)\
42 class ConfigCreate(object):
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')
53 def __init__(self, uplink_ports, downlink_ports, socket):
54 super(ConfigCreate, self).__init__()
58 self.uplink_ports = uplink_ports
59 self.downlink_ports = downlink_ports
60 self.pipeline_per_port = 9
63 def vpe_initialize(self, config):
64 config.add_section('EAL')
65 config.set('EAL', 'log_level', '0')
67 config.add_section('PIPELINE0')
68 config.set('PIPELINE0', 'type', 'MASTER')
69 config.set('PIPELINE0', 'core', 's%sC0' % self.socket)
71 config.add_section('MEMPOOL0')
72 config.set('MEMPOOL0', 'pool_size', '256K')
74 config.add_section('MEMPOOL1')
75 config.set('MEMPOOL1', 'pool_size', '2M')
78 def vpe_rxq(self, config):
79 for port in self.downlink_ports:
80 new_section = 'RXQ{0}.0'.format(port)
81 config.add_section(new_section)
82 config.set(new_section, 'mempool', 'MEMPOOL1')
86 def get_sink_swq(self, parser, pipeline, k, index):
88 pktq = parser.get(pipeline, k)
91 sink = " SINK{0}".format(self.sink_q)
93 sink = " TM{0}".format(index)
94 pktq = "SWQ{0}{1}".format(self.sw_q, sink)
97 def vpe_upstream(self, vnf_cfg, index=0):
98 parser = configparser.ConfigParser()
99 parser.read(os.path.join(vnf_cfg, 'vpe_upstream'))
101 for pipeline in parser.sections():
102 for k, v in parser.items(pipeline):
105 value = "RXQ{0}.0".format(self.uplink_ports[index])
107 value = self.get_sink_swq(parser, pipeline, k, index)
109 parser.set(pipeline, k, value)
111 elif k == "pktq_out":
113 value = "TXQ{0}.0".format(self.downlink_ports[index])
116 value = self.get_sink_swq(parser, pipeline, k, index)
118 parser.set(pipeline, k, value)
120 new_pipeline = 'PIPELINE{0}'.format(self.n_pipeline)
121 if new_pipeline != pipeline:
122 parser._sections[new_pipeline] = parser._sections[pipeline]
123 parser._sections.pop(pipeline)
127 def vpe_downstream(self, vnf_cfg, index):
128 parser = configparser.ConfigParser()
129 parser.read(os.path.join(vnf_cfg, 'vpe_downstream'))
130 for pipeline in parser.sections():
131 for k, v in parser.items(pipeline):
135 value = self.get_sink_swq(parser, pipeline, k, index)
137 value = "RXQ{0}.0 TM{1}".format(self.downlink_ports[index], index)
139 value = "RXQ{0}.0".format(self.downlink_ports[index])
141 parser.set(pipeline, k, value)
146 value = self.get_sink_swq(parser, pipeline, k, index)
148 value = "TXQ{0}.0 TM{1}".format(self.uplink_ports[index], index)
150 value = "TXQ{0}.0".format(self.uplink_ports[index])
152 parser.set(pipeline, k, value)
154 new_pipeline = 'PIPELINE{0}'.format(self.n_pipeline)
155 if new_pipeline != pipeline:
156 parser._sections[new_pipeline] = parser._sections[pipeline]
157 parser._sections.pop(pipeline)
161 def create_vpe_config(self, vnf_cfg):
162 config = configparser.ConfigParser()
163 vpe_cfg = os.path.join("/tmp/vpe_config")
164 with open(vpe_cfg, 'w') as cfg_file:
165 config = self.vpe_initialize(config)
166 config = self.vpe_rxq(config)
167 config.write(cfg_file)
168 for index in range(0, len(self.uplink_ports)):
169 config = self.vpe_upstream(vnf_cfg, index)
170 config.write(cfg_file)
171 config = self.vpe_downstream(vnf_cfg, index)
172 config = self.vpe_tmq(config, index)
173 config.write(cfg_file)
175 def generate_vpe_script(self, interfaces):
176 rules = PipelineRules(pipeline_id=1)
177 for priv_port, pub_port in zip(self.uplink_ports, self.downlink_ports):
178 priv_intf = interfaces[priv_port]["virtual-interface"]
179 pub_intf = interfaces[pub_port]["virtual-interface"]
181 dst_port0_ip = priv_intf["dst_ip"]
182 dst_port1_ip = pub_intf["dst_ip"]
183 dst_port0_mac = priv_intf["dst_mac"]
184 dst_port1_mac = pub_intf["dst_mac"]
186 rules.add_firewall_script(dst_port0_ip)
187 rules.next_pipeline()
188 rules.add_flow_classification_script()
189 rules.next_pipeline()
190 rules.add_flow_action()
191 rules.next_pipeline()
192 rules.add_flow_action2()
193 rules.next_pipeline()
194 rules.add_route_script(dst_port1_ip, dst_port1_mac)
195 rules.next_pipeline()
196 rules.add_route_script2(dst_port0_ip, dst_port0_mac)
197 rules.next_pipeline(num=4)
199 return rules.get_string()
201 def generate_tm_cfg(self, vnf_cfg, index=0):
202 vnf_cfg = os.path.join(vnf_cfg, "full_tm_profile_10G.cfg")
203 if os.path.exists(vnf_cfg):
204 return open(vnf_cfg).read()
207 class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper):
210 CFG_CONFIG = "/tmp/vpe_config"
211 CFG_SCRIPT = "/tmp/vpe_script"
212 TM_CONFIG = "/tmp/full_tm_profile_10G.cfg"
213 CORES = ['0', '1', '2', '3', '4', '5']
214 PIPELINE_COMMAND = VPE_PIPELINE_COMMAND
216 def _build_vnf_ports(self):
217 self._port_pairs = PortPairs(self.vnfd_helper.interfaces)
218 self.uplink_ports = self._port_pairs.uplink_ports
219 self.downlink_ports = self._port_pairs.downlink_ports
220 self.all_ports = self._port_pairs.all_ports
222 def build_config(self):
224 "bin_path": self.ssh_helper.bin_path,
225 "socket": self.socket,
228 self._build_vnf_ports()
229 vpe_conf = ConfigCreate(self.vnfd_helper.port_pairs.uplink_ports,
230 self.vnfd_helper.port_pairs.downlink_ports, self.socket)
231 vpe_conf.create_vpe_config(self.scenario_helper.vnf_cfg)
233 config_basename = posixpath.basename(self.CFG_CONFIG)
234 script_basename = posixpath.basename(self.CFG_SCRIPT)
235 tm_basename = posixpath.basename(self.TM_CONFIG)
236 with open(self.CFG_CONFIG) as handle:
237 vpe_config = handle.read()
239 self.ssh_helper.upload_config_file(config_basename, vpe_config.format(**vpe_vars))
241 vpe_script = vpe_conf.generate_vpe_script(self.vnfd_helper.interfaces)
242 self.ssh_helper.upload_config_file(script_basename, vpe_script.format(**vpe_vars))
244 tm_config = vpe_conf.generate_tm_cfg(self.scenario_helper.vnf_cfg)
245 self.ssh_helper.upload_config_file(tm_basename, tm_config)
247 LOG.info("Provision and start the %s", self.APP_NAME)
248 LOG.info(self.CFG_CONFIG)
249 LOG.info(self.CFG_SCRIPT)
250 self._build_pipeline_kwargs()
251 return self.PIPELINE_COMMAND.format(**self.pipeline_kwargs)
254 class VpeApproxVnf(SampleVNF):
255 """ This class handles vPE VNF model-driver definitions """
259 COLLECT_KPI = VPE_COLLECT_KPI
262 def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
263 if setup_env_helper_type is None:
264 setup_env_helper_type = VpeApproxSetupEnvHelper
266 super(VpeApproxVnf, self).__init__(name, vnfd, setup_env_helper_type, resource_helper_type)
268 def get_stats(self, *args, **kwargs):
269 raise NotImplementedError
271 def collect_kpi(self):
273 'pkt_in_up_stream': 0,
274 'pkt_drop_up_stream': 0,
275 'pkt_in_down_stream': 0,
276 'pkt_drop_down_stream': 0,
277 'collect_stats': self.resource_helper.collect_kpi(),
281 indexes_drop = [2, 3]
282 command = 'p {0} stats port {1} 0'
283 for index, direction in ((5, 'up'), (9, 'down')):
284 key_in = "pkt_in_{0}_stream".format(direction)
285 key_drop = "pkt_drop_{0}_stream".format(direction)
286 for mode in ('in', 'out'):
287 stats = self.vnf_execute(command.format(index, mode))
288 match = re.search(self.COLLECT_KPI, stats, re.MULTILINE)
291 result[key_in] += sum(int(match.group(x)) for x in indexes_in)
292 result[key_drop] += sum(int(match.group(x)) for x in indexes_drop)
294 LOG.debug("%s collect KPIs %s", self.APP_NAME, result)