Merge "Resolve full path to adminrc file if full path is not provided"
[yardstick.git] / yardstick / network_services / helpers / samplevnf_helper.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
15 from __future__ import absolute_import
16
17 import ipaddress
18 import logging
19 import os
20 import sys
21 from collections import OrderedDict, defaultdict
22 from itertools import chain, repeat
23
24 import six
25 from six.moves.configparser import ConfigParser
26
27 from yardstick.common.utils import ip_to_hex
28
29 LOG = logging.getLogger(__name__)
30
31 LINK_CONFIG_TEMPLATE = """\
32 link {0} down
33 link {0} config {1} {2}
34 link {0} up
35 """
36
37 ACTION_TEMPLATE = """\
38 p action add {0} accept
39 p action add {0} fwd {0}
40 p action add {0} count
41 """
42
43 FW_ACTION_TEMPLATE = """\
44 p action add {0} accept
45 p action add {0} fwd {0}
46 p action add {0} count
47 p action add {0} conntrack
48 """
49
50 # This sets up a basic passthrough with no rules
51 SCRIPT_TPL = """
52 {link_config}
53
54 {arp_config}
55
56 {arp_config6}
57
58 {arp_route_tbl}
59
60 {arp_route_tbl6}
61
62 {actions}
63
64 {rules}
65
66 """
67
68
69 class PortPairs(object):
70
71     DOWNLINK = "downlink"
72     UPLINK = "uplink"
73
74     def __init__(self, interfaces):
75         super(PortPairs, self).__init__()
76         self.interfaces = interfaces
77         self._all_ports = None
78         self._uplink_ports = None
79         self._downlink_ports = None
80         self._networks = None
81         self._port_pair_list = None
82         self._valid_networks = None
83
84     @property
85     def networks(self):
86         if self._networks is None:
87             self._networks = {}
88             for intf in self.interfaces:
89                 vintf = intf['virtual-interface']
90                 try:
91                     vld_id = vintf['vld_id']
92                 except KeyError:
93                     # probably unused port?
94                     LOG.warning("intf without vld_id, %s", vintf)
95                 else:
96                     self._networks.setdefault(vld_id, []).append(vintf["ifname"])
97         return self._networks
98
99     @classmethod
100     def get_downlink_id(cls, vld_id):
101         # partition returns a tuple
102         parts = list(vld_id.partition(cls.UPLINK))
103         if parts[0]:
104             # 'uplink' was not in or not leftmost in the string
105             return
106         parts[1] = cls.DOWNLINK
107         public_id = ''.join(parts)
108         return public_id
109
110     @property
111     # this only works for vnfs that have both uplink and public visible
112     def valid_networks(self):
113         if self._valid_networks is None:
114             self._valid_networks = []
115             for vld_id in self.networks:
116                 downlink_id = self.get_downlink_id(vld_id)
117                 if downlink_id in self.networks:
118                     self._valid_networks.append((vld_id, downlink_id))
119         return self._valid_networks
120
121     @property
122     def all_ports(self):
123         if self._all_ports is None:
124             self._all_ports = sorted(set(self.uplink_ports + self.downlink_ports))
125         return self._all_ports
126
127     @property
128     def uplink_ports(self):
129         if self._uplink_ports is None:
130             intfs = chain.from_iterable(
131                 intfs for vld_id, intfs in self.networks.items() if
132                 vld_id.startswith(self.UPLINK))
133             self._uplink_ports = sorted(set(intfs))
134         return self._uplink_ports
135
136     @property
137     def downlink_ports(self):
138         if self._downlink_ports is None:
139             intfs = chain.from_iterable(
140                 intfs for vld_id, intfs in self.networks.items() if
141                 vld_id.startswith(self.DOWNLINK))
142             self._downlink_ports = sorted(set(intfs))
143         return self._downlink_ports
144
145     @property
146     def port_pair_list(self):
147         if self._port_pair_list is None:
148             self._port_pair_list = []
149
150             for uplink, downlink in self.valid_networks:
151                 for uplink_intf in self.networks[uplink]:
152                     # only VNFs have uplink, public peers
153                     peer_intfs = self.networks.get(downlink, [])
154                     if peer_intfs:
155                         for downlink_intf in peer_intfs:
156                             port_pair = uplink_intf, downlink_intf
157                             self._port_pair_list.append(port_pair)
158         return self._port_pair_list
159
160
161 class MultiPortConfig(object):
162
163     HW_LB = "HW"
164
165     @staticmethod
166     def float_x_plus_one_tenth_of_y(x, y):
167         return float(x) + float(y) / 10.0
168
169     @staticmethod
170     def make_str(base, iterator):
171         return ' '.join((base.format(x) for x in iterator))
172
173     @classmethod
174     def make_range_str(cls, base, start, stop=0, offset=0):
175         if offset and not stop:
176             stop = start + offset
177         return cls.make_str(base, range(start, stop))
178
179     @staticmethod
180     def parser_get(parser, section, key, default=None):
181         if parser.has_option(section, key):
182             return parser.get(section, key)
183         return default
184
185     @staticmethod
186     def make_ip_addr(ip, mask):
187         """
188         :param ip: ip adddress
189         :type ip: str
190         :param mask: /24 prefix of 255.255.255.0 netmask
191         :type mask: str
192         :return: interface
193         :rtype: IPv4Interface
194         """
195
196         try:
197             return ipaddress.ip_interface(six.text_type('/'.join([ip, mask])))
198         except (TypeError, ValueError):
199             # None so we can skip later
200             return None
201
202     @classmethod
203     def validate_ip_and_prefixlen(cls, ip_addr, prefixlen):
204         ip_addr = cls.make_ip_addr(ip_addr, prefixlen)
205         return ip_addr.ip.exploded, ip_addr.network.prefixlen
206
207     def __init__(self, topology_file, config_tpl, tmp_file, vnfd_helper,
208                  vnf_type='CGNAT', lb_count=2, worker_threads=3,
209                  worker_config='1C/1T', lb_config='SW', socket=0):
210
211         super(MultiPortConfig, self).__init__()
212         self.topology_file = topology_file
213         self.worker_config = worker_config.split('/')[1].lower()
214         self.worker_threads = self.get_worker_threads(worker_threads)
215         self.vnf_type = vnf_type
216         self.pipe_line = 0
217         self.vnfd_helper = vnfd_helper
218         self.write_parser = ConfigParser()
219         self.read_parser = ConfigParser()
220         self.read_parser.read(config_tpl)
221         self.master_core = self.read_parser.get("PIPELINE0", "core")
222         self.master_tpl = self.get_config_tpl_data('MASTER')
223         self.arpicmp_tpl = self.get_config_tpl_data('ARPICMP')
224         self.txrx_tpl = self.get_config_tpl_data('TXRX')
225         self.loadb_tpl = self.get_config_tpl_data('LOADB')
226         self.vnf_tpl = self.get_config_tpl_data(vnf_type)
227         self.swq = 0
228         self.lb_count = int(lb_count)
229         self.lb_config = lb_config
230         self.tmp_file = os.path.join("/tmp", tmp_file)
231         self.pktq_out_os = []
232         self.socket = socket
233         self.start_core = 1
234         self.pipeline_counter = ""
235         self.txrx_pipeline = ""
236         self._port_pairs = None
237         self.all_ports = []
238         self.port_pair_list = []
239         self.lb_to_port_pair_mapping = {}
240         self.init_eal()
241
242         self.lb_index = None
243         self.mul = 0
244         self.port_pairs = []
245         self.ports_len = 0
246         self.prv_que_handler = None
247         self.vnfd = None
248         self.rules = None
249         self.pktq_out = []
250
251     @staticmethod
252     def gen_core(core):
253         # return "s{}c{}".format(self.socket, core)
254         # don't use sockets for VNFs, because we don't want to have to
255         # adjust VM CPU topology.  It is virtual anyway
256         return str(core)
257
258     def make_port_pairs_iter(self, operand, iterable):
259         return (operand(self.vnfd_helper.port_num(x), y) for y in iterable for x in
260                 chain.from_iterable(self.port_pairs))
261
262     def make_range_port_pairs_iter(self, operand, start, end):
263         return self.make_port_pairs_iter(operand, range(start, end))
264
265     def init_eal(self):
266         lines = ['[EAL]\n']
267         vpci = (v['virtual-interface']["vpci"] for v in self.vnfd_helper.interfaces)
268         lines.extend('w = {0}\n'.format(item) for item in vpci)
269         lines.append('\n')
270         with open(self.tmp_file, 'w') as fh:
271             fh.writelines(lines)
272
273     def update_timer(self):
274         timer_tpl = self.get_config_tpl_data('TIMER')
275         timer_tpl['core'] = self.gen_core(0)
276         self.update_write_parser(timer_tpl)
277
278     def get_config_tpl_data(self, type_value):
279         for section in self.read_parser.sections():
280             if self.read_parser.has_option(section, 'type'):
281                 if type_value == self.read_parser.get(section, 'type'):
282                     tpl = OrderedDict(self.read_parser.items(section))
283                     return tpl
284
285     def get_txrx_tpl_data(self, value):
286         for section in self.read_parser.sections():
287             if self.read_parser.has_option(section, 'pipeline_txrx_type'):
288                 if value == self.read_parser.get(section, 'pipeline_txrx_type'):
289                     tpl = OrderedDict(self.read_parser.items(section))
290                     return tpl
291
292     def init_write_parser_template(self, type_value='ARPICMP'):
293         for section in self.read_parser.sections():
294             if type_value == self.parser_get(self.read_parser, section, 'type', object()):
295                 self.pipeline_counter = self.read_parser.getint(section, 'core')
296                 self.txrx_pipeline = self.read_parser.getint(section, 'core')
297                 return
298             self.write_parser.add_section(section)
299             for name, value in self.read_parser.items(section):
300                 self.write_parser.set(section, name, value)
301
302     def update_write_parser(self, data):
303         section = "PIPELINE{0}".format(self.pipeline_counter)
304         self.write_parser.add_section(section)
305         for name, value in data.items():
306             self.write_parser.set(section, name, value)
307
308     def get_worker_threads(self, worker_threads):
309         if self.worker_config == '1t':
310             return worker_threads
311         else:
312             return worker_threads - worker_threads % 2
313
314     def generate_next_core_id(self):
315         if self.worker_config == '1t':
316             self.start_core += 1
317             return
318
319         try:
320             self.start_core = '{}h'.format(int(self.start_core))
321         except ValueError:
322             self.start_core = int(self.start_core[:-1]) + 1
323
324     def get_lb_count(self):
325         self.lb_count = int(min(len(self.port_pair_list), self.lb_count))
326
327     def generate_lb_to_port_pair_mapping(self):
328         self.lb_to_port_pair_mapping = defaultdict(int)
329         port_pair_count = len(self.port_pair_list)
330         lb_pair_count = int(port_pair_count / self.lb_count)
331         extra = port_pair_count % self.lb_count
332         extra_iter = repeat(lb_pair_count + 1, extra)
333         norm_iter = repeat(lb_pair_count, port_pair_count - extra)
334         new_values = {i: v for i, v in enumerate(chain(extra_iter, norm_iter), 1)}
335         self.lb_to_port_pair_mapping.update(new_values)
336
337     def set_priv_to_pub_mapping(self):
338         port_nums = [tuple(self.vnfd_helper.port_nums(x)) for x in self.port_pair_list]
339         return "".join(str(y).replace(" ", "") for y in
340                        port_nums)
341
342     def set_priv_que_handler(self):
343         # iterated twice, can't be generator
344         priv_to_pub_map = [tuple(self.vnfd_helper.port_nums(x)) for x in self.port_pairs]
345         # must be list to use .index()
346         port_list = list(chain.from_iterable(priv_to_pub_map))
347         uplink_ports = (x[0] for x in priv_to_pub_map)
348         self.prv_que_handler = '({})'.format(
349             "".join(("{},".format(port_list.index(x)) for x in uplink_ports)))
350
351     def generate_arp_route_tbl(self):
352         arp_route_tbl_tmpl = "routeadd net {port_num} {port_dst_ip} 0x{port_netmask_hex}"
353
354         def build_arp_config(port):
355             dpdk_port_num = self.vnfd_helper.port_num(port)
356             interface = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
357             # We must use the dst because we are on the VNF and we need to
358             # reach the TG.
359             dst_port_ip = ipaddress.ip_interface(six.text_type(
360                 "%s/%s" % (interface["dst_ip"], interface["netmask"])))
361
362             arp_vars = {
363                 "port_netmask_hex": ip_to_hex(dst_port_ip.network.netmask.exploded),
364                 # this is the port num that contains port0 subnet and next_hop_ip_hex
365                 # this is LINKID which should be based on DPDK port number
366                 "port_num": dpdk_port_num,
367                 # next hop is dst in this case
368                 # must be within subnet
369                 "port_dst_ip": str(dst_port_ip.ip),
370             }
371             return arp_route_tbl_tmpl.format(**arp_vars)
372
373         return '\n'.join(build_arp_config(port) for port in self.all_ports)
374
375     def generate_arpicmp_data(self):
376         swq_in_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
377         self.swq += self.lb_count
378         swq_out_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
379         self.swq += self.lb_count
380         # ports_mac_list is disabled for some reason
381
382         # mac_iter = (self.vnfd_helper.find_interface(name=port)['virtual-interface']['local_mac']
383         #             for port in self.all_ports)
384         pktq_in_iter = ('RXQ{}.0'.format(self.vnfd_helper.port_num(x[0])) for x in
385                         self.port_pair_list)
386
387         arpicmp_data = {
388             'core': self.gen_core(0),
389             'pktq_in': swq_in_str,
390             'pktq_out': swq_out_str,
391             # we need to disable ports_mac_list?
392             # it looks like ports_mac_list is no longer required
393             # 'ports_mac_list': ' '.join(mac_iter),
394             'pktq_in_prv': ' '.join(pktq_in_iter),
395             'prv_to_pub_map': self.set_priv_to_pub_mapping(),
396         }
397         self.pktq_out_os = swq_out_str.split(' ')
398         # HWLB is a run to complition. So override the pktq_in/pktq_out
399         if self.lb_config == self.HW_LB:
400             self.swq = 0
401             swq_in_str = \
402                 self.make_range_str('SWQ{}', self.swq,
403                                     offset=(self.lb_count * self.worker_threads))
404             arpicmp_data['pktq_in'] = swq_in_str
405             # WA: Since port_pairs will not be populated during arp pipeline
406             self.port_pairs = self.port_pair_list
407             port_iter = \
408                 self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
409             pktq_out = self.make_str('TXQ{}', port_iter)
410             arpicmp_data['pktq_out'] = pktq_out
411
412         return arpicmp_data
413
414     def generate_final_txrx_data(self, core=0):
415         swq_start = self.swq - self.ports_len * self.worker_threads
416
417         txq_start = 0
418         txq_end = self.worker_threads
419
420         pktq_out_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
421                                                         txq_start, txq_end)
422
423         swq_str = self.make_range_str('SWQ{}', swq_start, self.swq)
424         txq_str = self.make_str('TXQ{}', pktq_out_iter)
425         rxtx_data = {
426             'pktq_in': swq_str,
427             'pktq_out': txq_str,
428             'pipeline_txrx_type': 'TXTX',
429             'core': self.gen_core(core),
430         }
431         pktq_in = rxtx_data['pktq_in']
432         pktq_in = '{0} {1}'.format(pktq_in, self.pktq_out_os[self.lb_index - 1])
433         rxtx_data['pktq_in'] = pktq_in
434         self.pipeline_counter += 1
435         return rxtx_data
436
437     def generate_initial_txrx_data(self):
438         pktq_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
439                                                     0, self.worker_threads)
440
441         rxq_str = self.make_str('RXQ{}', pktq_iter)
442         swq_str = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
443         txrx_data = {
444             'pktq_in': rxq_str,
445             'pktq_out': swq_str + ' SWQ{0}'.format(self.lb_index - 1),
446             'pipeline_txrx_type': 'RXRX',
447             'core': self.gen_core(self.start_core),
448         }
449         self.pipeline_counter += 1
450         return self.start_core, txrx_data
451
452     def generate_lb_data(self):
453         pktq_in = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
454         self.swq += self.ports_len
455
456         offset = self.ports_len * self.worker_threads
457         pktq_out = self.make_range_str('SWQ{}', self.swq, offset=offset)
458         self.pktq_out = pktq_out.split()
459
460         self.swq += (self.ports_len * self.worker_threads)
461         lb_data = {
462             'prv_que_handler': self.prv_que_handler,
463             'pktq_in': pktq_in,
464             'pktq_out': pktq_out,
465             'n_vnf_threads': str(self.worker_threads),
466             'core': self.gen_core(self.start_core),
467         }
468         self.pipeline_counter += 1
469         return lb_data
470
471     def generate_vnf_data(self):
472         if self.lb_config == self.HW_LB:
473             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
474             pktq_in = self.make_str('RXQ{}', port_iter)
475
476             self.mul += 1
477             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
478             pktq_out = self.make_str('TXQ{}', port_iter)
479
480             pipe_line_data = {
481                 'pktq_in': pktq_in,
482                 'pktq_out': pktq_out + ' SWQ{0}'.format(self.swq),
483                 'prv_que_handler': self.prv_que_handler,
484                 'core': self.gen_core(self.start_core),
485             }
486             self.swq += 1
487         else:
488             pipe_line_data = {
489                 'pktq_in': ' '.join((self.pktq_out.pop(0) for _ in range(self.ports_len))),
490                 'pktq_out': self.make_range_str('SWQ{}', self.swq, offset=self.ports_len),
491                 'prv_que_handler': self.prv_que_handler,
492                 'core': self.gen_core(self.start_core),
493             }
494             self.swq += self.ports_len
495
496         if self.vnf_type in ('ACL', 'VFW'):
497             pipe_line_data.pop('prv_que_handler')
498
499         if self.vnf_tpl.get('vnf_set'):
500             public_ip_port_range_list = self.vnf_tpl['public_ip_port_range'].split(':')
501             ip_in_hex = '{:x}'.format(int(public_ip_port_range_list[0], 16) + self.lb_index - 1)
502             public_ip_port_range_list[0] = ip_in_hex
503             self.vnf_tpl['public_ip_port_range'] = ':'.join(public_ip_port_range_list)
504
505         self.pipeline_counter += 1
506         return pipe_line_data
507
508     def generate_config_data(self):
509         self.init_write_parser_template()
510
511         # use master core for master, don't use self.start_core
512         self.write_parser.set('PIPELINE0', 'core', self.gen_core(self.master_core))
513         arpicmp_data = self.generate_arpicmp_data()
514         self.arpicmp_tpl.update(arpicmp_data)
515         self.update_write_parser(self.arpicmp_tpl)
516
517         if self.vnf_type == 'CGNAPT':
518             self.pipeline_counter += 1
519             self.update_timer()
520
521         if self.lb_config == 'HW':
522             self.start_core = 1
523
524         for lb in self.lb_to_port_pair_mapping:
525             self.lb_index = lb
526             self.mul = 0
527             port_pair_count = self.lb_to_port_pair_mapping[lb]
528             if not self.port_pair_list:
529                 continue
530
531             self.port_pairs = self.port_pair_list[:port_pair_count]
532             self.port_pair_list = self.port_pair_list[port_pair_count:]
533             self.ports_len = port_pair_count * 2
534             self.set_priv_que_handler()
535             if self.lb_config == 'SW':
536                 core, txrx_data = self.generate_initial_txrx_data()
537                 self.txrx_tpl.update(txrx_data)
538                 self.update_write_parser(self.txrx_tpl)
539                 self.start_core += 1
540                 lb_data = self.generate_lb_data()
541                 self.loadb_tpl.update(lb_data)
542                 self.update_write_parser(self.loadb_tpl)
543                 self.start_core += 1
544
545             for i in range(self.worker_threads):
546                 vnf_data = self.generate_vnf_data()
547                 if not self.vnf_tpl:
548                     self.vnf_tpl = {}
549                 self.vnf_tpl.update(vnf_data)
550                 self.update_write_parser(self.vnf_tpl)
551                 try:
552                     self.vnf_tpl.pop('vnf_set')
553                 except KeyError:
554                     pass
555                 else:
556                     self.vnf_tpl.pop('public_ip_port_range')
557                 self.generate_next_core_id()
558
559             if self.lb_config == 'SW':
560                 txrx_data = self.generate_final_txrx_data(core)
561                 self.txrx_tpl.update(txrx_data)
562                 self.update_write_parser(self.txrx_tpl)
563             self.vnf_tpl = self.get_config_tpl_data(self.vnf_type)
564
565     def generate_config(self):
566         self._port_pairs = PortPairs(self.vnfd_helper.interfaces)
567         self.port_pair_list = self._port_pairs.port_pair_list
568         self.all_ports = self._port_pairs.all_ports
569
570         self.get_lb_count()
571         self.generate_lb_to_port_pair_mapping()
572         self.generate_config_data()
573         self.write_parser.write(sys.stdout)
574         with open(self.tmp_file, 'a') as tfh:
575             self.write_parser.write(tfh)
576
577     def generate_link_config(self):
578         def build_args(port):
579             # lookup interface by name
580             virtual_interface = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
581             local_ip = virtual_interface["local_ip"]
582             netmask = virtual_interface["netmask"]
583             port_num = self.vnfd_helper.port_num(port)
584             port_ip, prefix_len = self.validate_ip_and_prefixlen(local_ip, netmask)
585             return LINK_CONFIG_TEMPLATE.format(port_num, port_ip, prefix_len)
586
587         return ''.join(build_args(port) for port in self.all_ports)
588
589     def get_route_data(self, src_key, data_key, port):
590         route_list = self.vnfd['vdu'][0].get(src_key, [])
591         try:
592             return next((route[data_key] for route in route_list if route['if'] == port), None)
593         except (TypeError, StopIteration, KeyError):
594             return None
595
596     def get_ports_gateway(self, port):
597         return self.get_route_data('routing_table', 'gateway', port)
598
599     def get_ports_gateway6(self, port):
600         return self.get_route_data('nd_route_tbl', 'gateway', port)
601
602     def get_netmask_gateway(self, port):
603         return self.get_route_data('routing_table', 'netmask', port)
604
605     def get_netmask_gateway6(self, port):
606         return self.get_route_data('nd_route_tbl', 'netmask', port)
607
608     def generate_arp_config(self):
609         arp_config = []
610         for port in self.all_ports:
611             # ignore gateway, always use TG IP
612             # gateway = self.get_ports_gateway(port)
613             vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
614             dst_mac = vintf["dst_mac"]
615             dst_ip = vintf["dst_ip"]
616             # arp_config.append(
617             #     (self.vnfd_helper.port_num(port), gateway, dst_mac, self.txrx_pipeline))
618             # so dst_mac is the TG dest mac, so we need TG dest IP.
619             # should be dpdk_port_num
620             arp_config.append(
621                 (self.vnfd_helper.port_num(port), dst_ip, dst_mac, self.txrx_pipeline))
622
623         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config))
624
625     def generate_arp_config6(self):
626         arp_config6 = []
627         for port in self.all_ports:
628             # ignore gateway, always use TG IP
629             # gateway6 = self.get_ports_gateway6(port)
630             vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
631             dst_mac6 = vintf["dst_mac"]
632             dst_ip6 = vintf["dst_ip"]
633             # arp_config6.append(
634             #     (self.vnfd_helper.port_num(port), gateway6, dst_mac6, self.txrx_pipeline))
635             arp_config6.append(
636                 (self.vnfd_helper.port_num(port), dst_ip6, dst_mac6, self.txrx_pipeline))
637
638         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config6))
639
640     def generate_action_config(self):
641         port_list = (self.vnfd_helper.port_num(p) for p in self.all_ports)
642         if self.vnf_type == "VFW":
643             template = FW_ACTION_TEMPLATE
644         else:
645             template = ACTION_TEMPLATE
646
647         return ''.join((template.format(port) for port in port_list))
648
649     def get_ip_from_port(self, port):
650         # we can't use gateway because in OpenStack gateways interfer with floating ip routing
651         # return self.make_ip_addr(self.get_ports_gateway(port), self.get_netmask_gateway(port))
652         vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
653         ip = vintf["local_ip"]
654         netmask = vintf["netmask"]
655         return self.make_ip_addr(ip, netmask)
656
657     def get_network_and_prefixlen_from_ip_of_port(self, port):
658         ip_addr = self.get_ip_from_port(port)
659         # handle cases with no gateway
660         if ip_addr:
661             return ip_addr.network.network_address.exploded, ip_addr.network.prefixlen
662         else:
663             return None, None
664
665     def generate_rule_config(self):
666         cmd = 'acl' if self.vnf_type == "ACL" else "vfw"
667         rules_config = self.rules if self.rules else ''
668         new_rules = []
669         new_ipv6_rules = []
670         pattern = 'p {0} add {1} {2} {3} {4} {5} 0 65535 0 65535 0 0 {6}'
671         for src_intf, dst_intf in self.port_pair_list:
672             src_port = self.vnfd_helper.port_num(src_intf)
673             dst_port = self.vnfd_helper.port_num(dst_intf)
674
675             src_net, src_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(src_intf)
676             dst_net, dst_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(dst_intf)
677             # ignore entires with empty values
678             if all((src_net, src_prefix_len, dst_net, dst_prefix_len)):
679                 new_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len,
680                                   dst_net, dst_prefix_len, dst_port))
681                 new_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len,
682                                   src_net, src_prefix_len, src_port))
683
684             # src_net = self.get_ports_gateway6(port_pair[0])
685             # src_prefix_len = self.get_netmask_gateway6(port_pair[0])
686             # dst_net = self.get_ports_gateway6(port_pair[1])
687             # dst_prefix_len = self.get_netmask_gateway6(port_pair[0])
688             # # ignore entires with empty values
689             # if all((src_net, src_prefix_len, dst_net, dst_prefix_len)):
690             #     new_ipv6_rules.append((cmd, self.txrx_pipeline, src_net, src_prefix_len,
691             #                            dst_net, dst_prefix_len, dst_port))
692             #     new_ipv6_rules.append((cmd, self.txrx_pipeline, dst_net, dst_prefix_len,
693             #                            src_net, src_prefix_len, src_port))
694
695         acl_apply = "\np %s applyruleset" % cmd
696         new_rules_config = '\n'.join(pattern.format(*values) for values
697                                      in chain(new_rules, new_ipv6_rules))
698         return ''.join([rules_config, new_rules_config, acl_apply])
699
700     def generate_script_data(self):
701         self._port_pairs = PortPairs(self.vnfd_helper.interfaces)
702         self.port_pair_list = self._port_pairs.port_pair_list
703         self.get_lb_count()
704         script_data = {
705             'link_config': self.generate_link_config(),
706             'arp_config': self.generate_arp_config(),
707             # disable IPv6 for now
708             # 'arp_config6': self.generate_arp_config6(),
709             'arp_config6': "",
710             'arp_config': self.generate_arp_config(),
711             'arp_route_tbl': self.generate_arp_route_tbl(),
712             'arp_route_tbl6': "",
713             'actions': '',
714             'rules': '',
715         }
716
717         if self.vnf_type in ('ACL', 'VFW'):
718             script_data.update({
719                 'actions': self.generate_action_config(),
720                 'rules': self.generate_rule_config(),
721             })
722
723         return script_data
724
725     def generate_script(self, vnfd, rules=None):
726         self.vnfd = vnfd
727         self.rules = rules
728         script_data = self.generate_script_data()
729         script = SCRIPT_TPL.format(**script_data)
730         if self.lb_config == self.HW_LB:
731             script += 'set fwd rxonly'
732             hwlb_tpl = """
733 set_sym_hash_ena_per_port {0} enable
734 set_hash_global_config {0} simple_xor ipv4-udp enable
735 set_sym_hash_ena_per_port {1} enable
736 set_hash_global_config {1} simple_xor ipv4-udp enable
737 set_hash_input_set {0} ipv4-udp src-ipv4 udp-src-port add
738 set_hash_input_set {1} ipv4-udp dst-ipv4 udp-dst-port add
739 set_hash_input_set {0} ipv6-udp src-ipv6 udp-src-port add
740 set_hash_input_set {1} ipv6-udp dst-ipv6 udp-dst-port add
741 """
742             for port_pair in self.port_pair_list:
743                 script += hwlb_tpl.format(*(self.vnfd_helper.port_nums(port_pair)))
744         return script