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