1 # Copyright (c) 2016-2019 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.
18 from yardstick.common import utils
19 from yardstick.common import exceptions
21 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, DpdkVnfSetupEnvHelper
22 from yardstick.network_services.helpers.samplevnf_helper import PortPairs
23 from itertools import chain
25 LOG = logging.getLogger(__name__)
27 # ACL should work the same on all systems, we can provide the binary
28 ACL_PIPELINE_COMMAND = \
29 'sudo {tool_path} -p {port_mask_hex} -f {cfg_file} -s {script} {hwlb}'
31 ACL_COLLECT_KPI = r"""\
32 ACL TOTAL:[^p]+pkts_processed"?:\s(\d+),[^p]+pkts_drop"?:\s(\d+),[^p]+pkts_received"?:\s(\d+),"""
35 class AclApproxSetupEnvSetupEnvHelper(DpdkVnfSetupEnvHelper):
38 CFG_CONFIG = "/tmp/acl_config"
39 CFG_SCRIPT = "/tmp/acl_script"
40 PIPELINE_COMMAND = ACL_PIPELINE_COMMAND
43 DEFAULT_CONFIG_TPL_CFG = "acl.cfg"
49 DEFAULT_PROTOCOL_MASK = 0
50 # Default actions to be applied to SampleVNF. Please note,
51 # that this list is extended with `fwd` action when default
52 # actions are generated.
53 DEFAULT_FWD_ACTIONS = ["accept", "count"]
55 def __init__(self, vnfd_helper, ssh_helper, scenario_helper):
56 super(AclApproxSetupEnvSetupEnvHelper, self).__init__(vnfd_helper,
61 def get_ip_from_port(self, port):
62 # we can't use gateway because in OpenStack gateways interfere with floating ip routing
63 # return self.make_ip_addr(self.get_ports_gateway(port), self.get_netmask_gateway(port))
64 vintf = self.vnfd_helper.find_interface(name=port)["virtual-interface"]
65 return utils.make_ip_addr(vintf["local_ip"], vintf["netmask"])
67 def get_network_and_prefixlen_from_ip_of_port(self, port):
68 ip_addr = self.get_ip_from_port(port)
69 # handle cases with no gateway
71 return ip_addr.network.network_address.exploded, ip_addr.network.prefixlen
76 def new_action_id(self):
77 """Get new action id"""
79 return self._action_id
81 def get_default_flows(self):
82 """Get default actions/rules
83 Returns: (<actions>, <rules>)
85 { <action_id>: [ <list of actions> ]}
87 { 0 : [ "accept", "count", {"fwd" : "port": 0} ], ... }
89 [ {"src_ip": "x.x.x.x", "src_ip_mask", 24, ...}, ... ]
91 See `generate_rule_cmds()` to get list of possible map keys.
93 actions, rules = {}, []
94 _port_pairs = PortPairs(self.vnfd_helper.interfaces)
95 port_pair_list = _port_pairs.port_pair_list
96 for src_intf, dst_intf in port_pair_list:
97 # get port numbers of the interfaces
98 src_port = self.vnfd_helper.port_num(src_intf)
99 dst_port = self.vnfd_helper.port_num(dst_intf)
100 # get interface addresses and prefixes
101 src_net, src_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(src_intf)
102 dst_net, dst_prefix_len = self.get_network_and_prefixlen_from_ip_of_port(dst_intf)
103 # ignore entries with empty values
104 if all((src_net, src_prefix_len, dst_net, dst_prefix_len)):
105 # flow: src_net:dst_net -> dst_port
106 action_id = self.new_action_id
107 actions[action_id] = self.DEFAULT_FWD_ACTIONS[:]
108 actions[action_id].append({"fwd": {"port": dst_port}})
109 rules.append({"priority": 1, 'cmd': self.RULE_CMD,
110 "src_ip": src_net, "src_ip_mask": src_prefix_len,
111 "dst_ip": dst_net, "dst_ip_mask": dst_prefix_len,
112 "src_port_from": 0, "src_port_to": 65535,
113 "dst_port_from": 0, "dst_port_to": 65535,
114 "protocol": 0, "protocol_mask": 0,
115 "action_id": action_id})
116 # flow: dst_net:src_net -> src_port
117 action_id = self.new_action_id
118 actions[action_id] = self.DEFAULT_FWD_ACTIONS[:]
119 actions[action_id].append({"fwd": {"port": src_port}})
120 rules.append({"cmd":self.RULE_CMD, "priority": 1,
121 "src_ip": dst_net, "src_ip_mask": dst_prefix_len,
122 "dst_ip": src_net, "dst_ip_mask": src_prefix_len,
123 "src_port_from": 0, "src_port_to": 65535,
124 "dst_port_from": 0, "dst_port_to": 65535,
125 "protocol": 0, "protocol_mask": 0,
126 "action_id": action_id})
127 return actions, rules
129 def get_flows(self, options):
130 """Get actions/rules based on provided options.
131 The `options` is a dict representing the ACL rules configuration
132 file. Result is the same as described in `get_default_flows()`.
134 actions, rules = {}, []
135 for ace in options['access-list-entries']:
136 # Generate list of actions
137 action_id = self.new_action_id
138 actions[action_id] = ace['actions']
139 # Destination nestwork
140 matches = ace['matches']
141 dst_ipv4_net = matches['destination-ipv4-network']
142 dst_ipv4_net_ip = ipaddress.ip_interface(six.text_type(dst_ipv4_net))
144 src_ipv4_net = matches['source-ipv4-network']
145 src_ipv4_net_ip = ipaddress.ip_interface(six.text_type(src_ipv4_net))
147 rules.append({'action_id': action_id, 'cmd': self.RULE_CMD,
148 'dst_ip': dst_ipv4_net_ip.network.network_address.exploded,
149 'dst_ip_mask': dst_ipv4_net_ip.network.prefixlen,
150 'src_ip': src_ipv4_net_ip.network.network_address.exploded,
151 'src_ip_mask': src_ipv4_net_ip.network.prefixlen,
152 'dst_port_from': matches['destination-port-range']['lower-port'],
153 'dst_port_to': matches['destination-port-range']['upper-port'],
154 'src_port_from': matches['source-port-range']['lower-port'],
155 'src_port_to': matches['source-port-range']['upper-port'],
156 'priority': matches.get('priority', self.DEFAULT_PRIORITY),
157 'protocol': matches.get('protocol', self.DEFAULT_PROTOCOL),
158 'protocol_mask': matches.get('protocol_mask',
159 self.DEFAULT_PROTOCOL_MASK)
161 return actions, rules
163 def generate_rule_cmds(self, rules, apply_rules=False):
164 """Convert rules into list of SampleVNF CLI commands"""
165 rule_template = ("p {cmd} add {priority} {src_ip} {src_ip_mask} "
166 "{dst_ip} {dst_ip_mask} {src_port_from} {src_port_to} "
167 "{dst_port_from} {dst_port_to} {protocol} "
168 "{protocol_mask} {action_id}")
171 rule_cmd_list.append(rule_template.format(**rule))
173 # add command to apply all rules at the end
174 rule_cmd_list.append("p {cmd} applyruleset".format(cmd=self.RULE_CMD))
177 def generate_action_cmds(self, actions):
178 """Convert actions into list of SampleVNF CLI commands.
179 These method doesn't validate the provided list of actions. Supported
180 list of actions are limited by SampleVNF. Thus, the user should be
181 responsible to specify correct action name(s). Yardstick should take
182 the provided action by user and apply it to SampleVNF.
183 Anyway, some of the actions require addition parameters to be
184 specified. In case of `fwd` & `nat` action used have to specify
187 _action_template_map = {
188 "fwd": "p action add {action_id} fwd {port}",
189 "nat": "p action add {action_id} nat {port}"
192 for action_id, actions in actions.items():
193 for action in actions:
194 if isinstance(action, dict):
195 for action_name in action.keys():
196 # user provided an action name with addition options
197 # e.g.: {"fwd": {"port": 0}}
198 # format action CLI command and add it to the list
199 if action_name not in _action_template_map.keys():
200 raise exceptions.AclUnknownActionTemplate(
201 action_name=action_name)
202 template = _action_template_map[action_name]
204 action_cmd_list.append(template.format(
205 action_id=action_id, **action[action_name]))
206 except KeyError as exp:
207 raise exceptions.AclMissingActionArguments(
208 action_name=action_name,
209 action_param=exp.args[0])
211 # user provided an action name w/o addition options
212 # e.g.: "accept", "count"
213 action_cmd_list.append(
214 "p action add {action_id} {action}".format(
215 action_id=action_id, action=action))
216 return action_cmd_list
218 def get_flows_config(self, options=None):
219 """Get action/rules configuration commands (string) to be
220 applied to SampleVNF to configure ACL rules (flows).
222 action_cmd_list, rule_cmd_list = [], []
224 # if file name is set, read actions/rules from the file
225 actions, rules = self.get_flows(options)
226 action_cmd_list = self.generate_action_cmds(actions)
227 rule_cmd_list = self.generate_rule_cmds(rules)
228 # default actions/rules
229 dft_actions, dft_rules = self.get_default_flows()
230 dft_action_cmd_list = self.generate_action_cmds(dft_actions)
231 dft_rule_cmd_list = self.generate_rule_cmds(dft_rules, apply_rules=True)
232 # generate multi-line commands to add actions/rules
233 return '\n'.join(chain(action_cmd_list, dft_action_cmd_list,
234 rule_cmd_list, dft_rule_cmd_list))
237 class AclApproxVnf(SampleVNF):
241 COLLECT_KPI = ACL_COLLECT_KPI
246 'packets_dropped': 2,
249 def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
250 if setup_env_helper_type is None:
251 setup_env_helper_type = AclApproxSetupEnvSetupEnvHelper
253 super(AclApproxVnf, self).__init__(name, vnfd, setup_env_helper_type, resource_helper_type)
255 def wait_for_instantiate(self):
256 """Wait for VNF to initialize"""
257 self.wait_for_initialize()