543d814dfb07daae4a13557b8113eb20e9830ee7
[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
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
40 p action add {0} count
41 """
42
43 FW_ACTION_TEMPLATE = """\
44 p action add {0} accept
45 p action add {0} fwd
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 {actions}
59
60 {rules}
61
62 """
63
64
65 class MultiPortConfig(object):
66
67     HW_LB = "HW"
68
69     @staticmethod
70     def float_x_plus_one_tenth_of_y(x, y):
71         return float(x) + float(y) / 10.0
72
73     @staticmethod
74     def make_str(base, iterator):
75         return ' '.join((base.format(x) for x in iterator))
76
77     @classmethod
78     def make_range_str(cls, base, start, stop=0, offset=0):
79         if offset and not stop:
80             stop = start + offset
81         return cls.make_str(base, range(start, stop))
82
83     @staticmethod
84     def parser_get(parser, section, key, default=None):
85         if parser.has_option(section, key):
86             return parser.get(section, key)
87         return default
88
89     @staticmethod
90     def make_ip_addr(ip, mask_len):
91         try:
92             return ipaddress.ip_interface(six.text_type('/'.join([ip, mask_len])))
93         except ValueError:
94             # None so we can skip later
95             return None
96
97     @classmethod
98     def validate_ip_and_prefixlen(cls, ip_addr, prefixlen):
99         ip_addr = cls.make_ip_addr(ip_addr, prefixlen)
100         return ip_addr.ip.exploded, ip_addr.network.prefixlen
101
102     def __init__(self, topology_file, config_tpl, tmp_file, interfaces=None,
103                  vnf_type='CGNAT', lb_count=2, worker_threads=3,
104                  worker_config='1C/1T', lb_config='SW', socket=0):
105
106         super(MultiPortConfig, self).__init__()
107         self.topology_file = topology_file
108         self.worker_config = worker_config.split('/')[1].lower()
109         self.worker_threads = self.get_worker_threads(worker_threads)
110         self.vnf_type = vnf_type
111         self.pipe_line = 0
112         self.interfaces = interfaces if interfaces else {}
113         self.networks = {}
114         self.write_parser = ConfigParser()
115         self.read_parser = ConfigParser()
116         self.read_parser.read(config_tpl)
117         self.master_core = self.read_parser.get("PIPELINE0", "core")
118         self.master_tpl = self.get_config_tpl_data('MASTER')
119         self.arpicmp_tpl = self.get_config_tpl_data('ARPICMP')
120         self.txrx_tpl = self.get_config_tpl_data('TXRX')
121         self.loadb_tpl = self.get_config_tpl_data('LOADB')
122         self.vnf_tpl = self.get_config_tpl_data(vnf_type)
123         self.swq = 0
124         self.lb_count = int(lb_count)
125         self.lb_config = lb_config
126         self.tmp_file = os.path.join("/tmp", tmp_file)
127         self.pktq_out_os = []
128         self.socket = socket
129         self.start_core = ""
130         self.pipeline_counter = ""
131         self.txrx_pipeline = ""
132         self.port_pair_list = []
133         self.lb_to_port_pair_mapping = {}
134         self.init_eal()
135
136         self.lb_index = None
137         self.mul = 0
138         self.port_pairs = []
139         self.port_pair_list = []
140         self.ports_len = 0
141         self.prv_que_handler = None
142         self.vnfd = None
143         self.rules = None
144         self.pktq_out = ''
145
146     @staticmethod
147     def gen_core(core):
148         # return "s{}c{}".format(self.socket, core)
149         # don't use sockets for VNFs, because we don't want to have to
150         # adjust VM CPU topology.  It is virtual anyway
151         return str(core)
152
153     def make_port_pairs_iter(self, operand, iterable):
154         return (operand(x[-1], y) for y in iterable for x in chain(*self.port_pairs))
155
156     def make_range_port_pairs_iter(self, operand, start, end):
157         return self.make_port_pairs_iter(operand, range(start, end))
158
159     def init_eal(self):
160         vpci = [v['virtual-interface']["vpci"] for v in self.interfaces]
161         with open(self.tmp_file, 'w') as fh:
162             fh.write('[EAL]\n')
163             for item in vpci:
164                 fh.write('w = {0}\n'.format(item))
165             fh.write('\n')
166
167     def update_timer(self):
168         timer_tpl = self.get_config_tpl_data('TIMER')
169         timer_tpl['core'] = self.gen_core(self.start_core)
170         self.update_write_parser(timer_tpl)
171         self.start_core += 1
172
173     def get_config_tpl_data(self, type_value):
174         for section in self.read_parser.sections():
175             if self.read_parser.has_option(section, 'type'):
176                 if type_value == self.read_parser.get(section, 'type'):
177                     tpl = OrderedDict(self.read_parser.items(section))
178                     return tpl
179
180     def get_txrx_tpl_data(self, value):
181         for section in self.read_parser.sections():
182             if self.read_parser.has_option(section, 'pipeline_txrx_type'):
183                 if value == self.read_parser.get(section, 'pipeline_txrx_type'):
184                     tpl = OrderedDict(self.read_parser.items(section))
185                     return tpl
186
187     def init_write_parser_template(self, type_value='ARPICMP'):
188         for section in self.read_parser.sections():
189             if type_value == self.parser_get(self.read_parser, section, 'type', object()):
190                 self.start_core = self.read_parser.getint(section, 'core')
191                 self.pipeline_counter = self.read_parser.getint(section, 'core')
192                 self.txrx_pipeline = self.read_parser.getint(section, 'core')
193                 return
194             self.write_parser.add_section(section)
195             for name, value in self.read_parser.items(section):
196                 self.write_parser.set(section, name, value)
197
198     def update_write_parser(self, data):
199         section = "PIPELINE{0}".format(self.pipeline_counter)
200         self.write_parser.add_section(section)
201         for name, value in data.items():
202             self.write_parser.set(section, name, value)
203
204     def get_worker_threads(self, worker_threads):
205         if self.worker_config == '1t':
206             return worker_threads
207         else:
208             return worker_threads - worker_threads % 2
209
210     def generate_next_core_id(self):
211         if self.worker_config == '1t':
212             self.start_core += 1
213             return
214
215         try:
216             self.start_core = 'h{}'.format(int(self.start_core))
217         except ValueError:
218             self.start_core = int(self.start_core[:-1]) + 1
219
220     @staticmethod
221     def get_port_pairs(interfaces):
222         port_pair_list = []
223         networks = defaultdict(list)
224         for private_intf in interfaces:
225             vintf = private_intf['virtual-interface']
226             networks[vintf['vld_id']].append(vintf)
227
228         for name, net in networks.items():
229             # partition returns a tuple
230             parts = list(name.partition('private'))
231             if parts[0]:
232                 # 'private' was not in or not leftmost in the string
233                 continue
234             parts[1] = 'public'
235             public_id = ''.join(parts)
236             for private_intf in net:
237                 try:
238                     public_peer_intfs = networks[public_id]
239                 except KeyError:
240                     LOG.warning("private network without peer %s, %s not found", name, public_id)
241                     continue
242
243                 for public_intf in public_peer_intfs:
244                     port_pair = private_intf["ifname"], public_intf["ifname"]
245                     port_pair_list.append(port_pair)
246
247         return port_pair_list, networks
248
249     def get_lb_count(self):
250         self.lb_count = int(min(len(self.port_pair_list), self.lb_count))
251
252     def generate_lb_to_port_pair_mapping(self):
253         self.lb_to_port_pair_mapping = defaultdict(int)
254         port_pair_count = len(self.port_pair_list)
255         lb_pair_count = int(port_pair_count / self.lb_count)
256         for i in range(self.lb_count):
257             self.lb_to_port_pair_mapping[i + 1] = lb_pair_count
258         for i in range(port_pair_count % self.lb_count):
259             self.lb_to_port_pair_mapping[i + 1] += 1
260
261     def set_priv_to_pub_mapping(self):
262         return "".join(str(y) for y in [(int(x[0][-1]), int(x[1][-1])) for x in
263                                         self.port_pair_list])
264
265     def set_priv_que_handler(self):
266         # iterated twice, can't be generator
267         priv_to_pub_map = [(int(x[0][-1]), int(x[1][-1])) for x in self.port_pairs]
268         # must be list to use .index()
269         port_list = list(chain.from_iterable(priv_to_pub_map))
270         priv_ports = (x[0] for x in priv_to_pub_map)
271         self.prv_que_handler = '({})'.format(
272             ",".join((str(port_list.index(x)) for x in priv_ports)))
273
274     def generate_arp_route_tbl(self):
275         arp_config = []
276         arp_route_tbl_tmpl = "({port0_dst_ip_hex},{port0_netmask_hex},{port_num}," \
277                              "{next_hop_ip_hex})"
278         for port_pair in self.port_pair_list:
279             for port in port_pair:
280                 port_num = int(port[-1])
281                 interface = self.interfaces[port_num]
282                 # port0_ip = ipaddress.ip_interface(six.text_type(
283                 #     "%s/%s" % (interface["virtual-interface"]["local_ip"],
284                 #                interface["virtual-interface"]["netmask"])))
285                 dst_port0_ip = \
286                     ipaddress.ip_interface(six.text_type(
287                         "%s/%s" % (interface["virtual-interface"]["dst_ip"],
288                                    interface["virtual-interface"]["netmask"])))
289                 arp_vars = {
290                     "port0_dst_ip_hex": ip_to_hex(dst_port0_ip.ip.exploded),
291                     "port0_netmask_hex": ip_to_hex(dst_port0_ip.network.netmask.exploded),
292                     "port_num": port_num,
293                     # next hop is dst in this case
294                     "next_hop_ip_hex": ip_to_hex(dst_port0_ip.ip.exploded),
295                 }
296                 arp_config.append(arp_route_tbl_tmpl.format(**arp_vars))
297
298         return ' '.join(arp_config)
299
300     def generate_arpicmp_data(self):
301         swq_in_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
302         self.swq += self.lb_count
303         swq_out_str = self.make_range_str('SWQ{}', self.swq, offset=self.lb_count)
304         self.swq += self.lb_count
305         mac_iter = (self.interfaces[int(x[-1])]['virtual-interface']['local_mac']
306                     for port_pair in self.port_pair_list for x in port_pair)
307         pktq_in_iter = ('RXQ{}'.format(float(x[0][-1])) for x in self.port_pair_list)
308
309         arpicmp_data = {
310             'core': self.gen_core(self.start_core),
311             'pktq_in': swq_in_str,
312             'pktq_out': swq_out_str,
313             'ports_mac_list': ' '.join(mac_iter),
314             'pktq_in_prv': ' '.join(pktq_in_iter),
315             'prv_to_pub_map': self.set_priv_to_pub_mapping(),
316             'arp_route_tbl': self.generate_arp_route_tbl(),
317             # can't use empty string, defaul to ()
318             'nd_route_tbl': "()",
319         }
320         self.pktq_out_os = swq_out_str.split(' ')
321         # why?
322         if self.lb_config == self.HW_LB:
323             arpicmp_data['pktq_in'] = swq_in_str
324             self.swq = 0
325         return arpicmp_data
326
327     def generate_final_txrx_data(self):
328         swq_start = self.swq - self.ports_len * self.worker_threads
329
330         txq_start = 0
331         txq_end = self.worker_threads
332
333         pktq_out_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
334                                                         txq_start, txq_end)
335
336         swq_str = self.make_range_str('SWQ{}', swq_start, self.swq)
337         txq_str = self.make_str('TXQ{}', pktq_out_iter)
338         rxtx_data = {
339             'pktq_in': swq_str,
340             'pktq_out': txq_str,
341             'pipeline_txrx_type': 'TXTX',
342             'core': self.gen_core(self.start_core),
343         }
344         pktq_in = rxtx_data['pktq_in']
345         pktq_in = '{0} {1}'.format(pktq_in, self.pktq_out_os[self.lb_index - 1])
346         rxtx_data['pktq_in'] = pktq_in
347         self.pipeline_counter += 1
348         return rxtx_data
349
350     def generate_initial_txrx_data(self):
351         pktq_iter = self.make_range_port_pairs_iter(self.float_x_plus_one_tenth_of_y,
352                                                     0, self.worker_threads)
353
354         rxq_str = self.make_str('RXQ{}', pktq_iter)
355         swq_str = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
356         txrx_data = {
357             'pktq_in': rxq_str,
358             'pktq_out': swq_str + ' SWQ{0}'.format(self.lb_index - 1),
359             'pipeline_txrx_type': 'RXRX',
360             'core': self.gen_core(self.start_core),
361         }
362         self.pipeline_counter += 1
363         return txrx_data
364
365     def generate_lb_data(self):
366         pktq_in = self.make_range_str('SWQ{}', self.swq, offset=self.ports_len)
367         self.swq += self.ports_len
368
369         offset = self.ports_len * self.worker_threads
370         pktq_out = self.make_range_str('SWQ{}', self.swq, offset=offset)
371         self.pktq_out = pktq_out.split()
372
373         self.swq += (self.ports_len * self.worker_threads)
374         lb_data = {
375             'prv_que_handler': self.prv_que_handler,
376             'pktq_in': pktq_in,
377             'pktq_out': pktq_out,
378             'n_vnf_threads': str(self.worker_threads),
379             'core': self.gen_core(self.start_core),
380         }
381         self.pipeline_counter += 1
382         return lb_data
383
384     def generate_vnf_data(self):
385         if self.lb_config == self.HW_LB:
386             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
387             pktq_in = self.make_str('RXQ{}', port_iter)
388
389             self.mul += 1
390             port_iter = self.make_port_pairs_iter(self.float_x_plus_one_tenth_of_y, [self.mul])
391             pktq_out = self.make_str('TXQ{}', port_iter)
392
393             pipe_line_data = {
394                 'pktq_in': pktq_in,
395                 'pktq_out': pktq_out + ' SWQ{0}'.format(self.swq),
396                 'prv_que_handler': self.prv_que_handler,
397                 'core': self.gen_core(self.start_core),
398             }
399             self.swq += 1
400         else:
401             pipe_line_data = {
402                 'pktq_in': ' '.join((self.pktq_out.pop(0) for _ in range(self.ports_len))),
403                 'pktq_out': self.make_range_str('SWQ{}', self.swq, offset=self.ports_len),
404                 'prv_que_handler': self.prv_que_handler,
405                 'core': self.gen_core(self.start_core),
406             }
407             self.swq += self.ports_len
408
409         if self.vnf_type in ('ACL', 'VFW'):
410             pipe_line_data.pop('prv_que_handler')
411
412         if self.vnf_tpl.get('vnf_set'):
413             public_ip_port_range_list = self.vnf_tpl['public_ip_port_range'].split(':')
414             ip_in_hex = '{:x}'.format(int(public_ip_port_range_list[0], 16) + self.lb_index - 1)
415             public_ip_port_range_list[0] = ip_in_hex
416             self.vnf_tpl['public_ip_port_range'] = ':'.join(public_ip_port_range_list)
417
418         self.pipeline_counter += 1
419         return pipe_line_data
420
421     def generate_config_data(self):
422         self.init_write_parser_template()
423
424         # use master core for master, don't use self.start_core
425         self.write_parser.set('PIPELINE0', 'core', self.gen_core(self.master_core))
426         arpicmp_data = self.generate_arpicmp_data()
427         self.arpicmp_tpl.update(arpicmp_data)
428         self.update_write_parser(self.arpicmp_tpl)
429
430         self.start_core += 1
431         if self.vnf_type == 'CGNAPT':
432             self.pipeline_counter += 1
433             self.update_timer()
434
435         for lb in self.lb_to_port_pair_mapping:
436             self.lb_index = lb
437             self.mul = 0
438             port_pair_count = self.lb_to_port_pair_mapping[lb]
439             if not self.port_pair_list:
440                 continue
441
442             self.port_pairs = self.port_pair_list[:port_pair_count]
443             self.port_pair_list = self.port_pair_list[port_pair_count:]
444             self.ports_len = port_pair_count * 2
445             self.set_priv_que_handler()
446             if self.lb_config == 'SW':
447                 txrx_data = self.generate_initial_txrx_data()
448                 self.txrx_tpl.update(txrx_data)
449                 self.update_write_parser(self.txrx_tpl)
450                 self.start_core += 1
451                 lb_data = self.generate_lb_data()
452                 self.loadb_tpl.update(lb_data)
453                 self.update_write_parser(self.loadb_tpl)
454                 self.start_core += 1
455
456             for i in range(self.worker_threads):
457                 vnf_data = self.generate_vnf_data()
458                 if not self.vnf_tpl:
459                     self.vnf_tpl = {}
460                 self.vnf_tpl.update(vnf_data)
461                 self.update_write_parser(self.vnf_tpl)
462                 try:
463                     self.vnf_tpl.pop('vnf_set')
464                 except KeyError:
465                     pass
466                 else:
467                     self.vnf_tpl.pop('public_ip_port_range')
468                 self.generate_next_core_id()
469
470             if self.lb_config == 'SW':
471                 txrx_data = self.generate_final_txrx_data()
472                 self.txrx_tpl.update(txrx_data)
473                 self.update_write_parser(self.txrx_tpl)
474                 self.start_core += 1
475             self.vnf_tpl = self.get_config_tpl_data(self.vnf_type)
476
477     def generate_config(self):
478         self.port_pair_list, self.networks = self.get_port_pairs(self.interfaces)
479         self.get_lb_count()
480         self.generate_lb_to_port_pair_mapping()
481         self.generate_config_data()
482         self.write_parser.write(sys.stdout)
483         with open(self.tmp_file, 'a') as tfh:
484             self.write_parser.write(tfh)
485
486     def generate_link_config(self):
487
488         link_configs = []
489         for port_pair in self.port_pair_list:
490             for port in port_pair:
491                 port = port[-1]
492                 virtual_interface = self.interfaces[int(port)]["virtual-interface"]
493                 local_ip = virtual_interface["local_ip"]
494                 netmask = virtual_interface["netmask"]
495                 port_ip, prefix_len = self.validate_ip_and_prefixlen(local_ip, netmask)
496                 link_configs.append(LINK_CONFIG_TEMPLATE.format(port, port_ip, prefix_len))
497
498         return ''.join(link_configs)
499
500     def get_route_data(self, src_key, data_key, port):
501         route_list = self.vnfd['vdu'][0].get(src_key, [])
502         try:
503             return next((route[data_key] for route in route_list if route['if'] == port), None)
504         except (TypeError, StopIteration, KeyError):
505             return None
506
507     def get_ports_gateway(self, port):
508         return self.get_route_data('routing_table', 'gateway', port)
509
510     def get_ports_gateway6(self, port):
511         return self.get_route_data('nd_route_tbl', 'gateway', port)
512
513     def get_netmask_gateway(self, port):
514         return self.get_route_data('routing_table', 'netmask', port)
515
516     def get_netmask_gateway6(self, port):
517         return self.get_route_data('nd_route_tbl', 'netmask', port)
518
519     def generate_arp_config(self):
520         arp_config = []
521         for port_pair in self.port_pair_list:
522             for port in port_pair:
523                 gateway = self.get_ports_gateway(port)
524                 # omit entries with no gateway
525                 if not gateway:
526                     continue
527                 dst_mac = self.interfaces[int(port[-1])]["virtual-interface"]["dst_mac"]
528                 arp_config.append((port[-1], gateway, dst_mac, self.txrx_pipeline))
529
530         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config))
531
532     def generate_arp_config6(self):
533         arp_config6 = []
534         for port_pair in self.port_pair_list:
535             for port in port_pair:
536                 gateway6 = self.get_ports_gateway6(port)
537                 # omit entries with no gateway
538                 if not gateway6:
539                     continue
540                 dst_mac6 = self.interfaces[int(port[-1])]["virtual-interface"]["dst_mac"]
541                 arp_config6.append((port[-1], gateway6, dst_mac6, self.txrx_pipeline))
542
543         return '\n'.join(('p {3} arpadd {0} {1} {2}'.format(*values) for values in arp_config6))
544
545     def generate_action_config(self):
546         port_list = []
547         for port_pair in self.port_pair_list:
548             for port in port_pair:
549                 port_list.append(port[-1])
550
551         if self.vnf_type == "VFW":
552             template = FW_ACTION_TEMPLATE
553         else:
554             template = ACTION_TEMPLATE
555
556         return ''.join((template.format(port) for port in port_list))
557
558     def get_ip_from_port(self, port):
559         return self.make_ip_addr(self.get_ports_gateway(port), self.get_netmask_gateway(port))
560
561     def get_ip_and_prefixlen_from_ip_of_port(self, port):
562         ip_addr = self.get_ip_from_port(port)
563         # handle cases with no gateway
564         if ip_addr:
565             return ip_addr.ip.exploded, ip_addr.network.prefixlen
566         else:
567             return None, None
568
569     def generate_rule_config(self):
570         cmd = 'acl' if self.vnf_type == "ACL" else "vfw"
571         rules_config = self.rules if self.rules else ''
572         new_rules = []
573         new_ipv6_rules = []
574         pattern = 'p {0} add {1} {2} {3} {4} {5} 0 65535 0 65535 0 0 {6}'
575         for port_pair in self.port_pair_list:
576             src_port = int(port_pair[0][-1])
577             dst_port = int(port_pair[1][-1])
578
579             src_ip, src_prefix_len = self.get_ip_and_prefixlen_from_ip_of_port(port_pair[0])
580             dst_ip, dst_prefix_len = self.get_ip_and_prefixlen_from_ip_of_port(port_pair[1])
581             # ignore entires with empty values
582             if all((src_ip, src_prefix_len, dst_ip, dst_prefix_len)):
583                 new_rules.append((cmd, self.txrx_pipeline, src_ip, src_prefix_len,
584                                   dst_ip, dst_prefix_len, dst_port))
585                 new_rules.append((cmd, self.txrx_pipeline, dst_ip, dst_prefix_len,
586                                   src_ip, src_prefix_len, src_port))
587
588             src_ip = self.get_ports_gateway6(port_pair[0])
589             src_prefix_len = self.get_netmask_gateway6(port_pair[0])
590             dst_ip = self.get_ports_gateway6(port_pair[1])
591             dst_prefix_len = self.get_netmask_gateway6(port_pair[0])
592             # ignore entires with empty values
593             if all((src_ip, src_prefix_len, dst_ip, dst_prefix_len)):
594                 new_ipv6_rules.append((cmd, self.txrx_pipeline, src_ip, src_prefix_len,
595                                        dst_ip, dst_prefix_len, dst_port))
596                 new_ipv6_rules.append((cmd, self.txrx_pipeline, dst_ip, dst_prefix_len,
597                                        src_ip, src_prefix_len, src_port))
598
599         acl_apply = "\np %s applyruleset" % cmd
600         new_rules_config = '\n'.join(pattern.format(*values) for values
601                                      in chain(new_rules, new_ipv6_rules))
602         return ''.join([rules_config, new_rules_config, acl_apply])
603
604     def generate_script_data(self):
605         self.port_pair_list, self.networks = self.get_port_pairs(self.interfaces)
606         self.get_lb_count()
607         script_data = {
608             'link_config': self.generate_link_config(),
609             'arp_config': self.generate_arp_config(),
610             'arp_config6': self.generate_arp_config6(),
611             'actions': '',
612             'rules': '',
613         }
614
615         if self.vnf_type in ('ACL', 'VFW'):
616             script_data.update({
617                 'actions': self.generate_action_config(),
618                 'rules': self.generate_rule_config(),
619             })
620
621         return script_data
622
623     def generate_script(self, vnfd, rules=None):
624         self.vnfd = vnfd
625         self.rules = rules
626         script_data = self.generate_script_data()
627         script = SCRIPT_TPL.format(**script_data)
628         if self.lb_config == self.HW_LB:
629             script += 'set fwd rxonly'
630             hwlb_tpl = """
631 set_sym_hash_ena_per_port {0} enable
632 set_hash_global_config {0} simple_xor ipv4-udp enable
633 set_sym_hash_ena_per_port {1} enable
634 set_hash_global_config {1} simple_xor ipv4-udp enable
635 set_hash_input_set {0} ipv4-udp src-ipv4 udp-src-port add
636 set_hash_input_set {1} ipv4-udp dst-ipv4 udp-dst-port add
637 set_hash_input_set {0} ipv6-udp src-ipv6 udp-src-port add
638 set_hash_input_set {1} ipv6-udp dst-ipv6 udp-dst-port add
639 """
640             for port_pair in self.port_pair_list:
641                 script += hwlb_tpl.format(port_pair[0][-1], port_pair[1][-1])
642         return script