Merge "Docs: Updated NSB installation: VM image build DUT"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / ipsec_vnf.py
1 # Copyright (c) 2019 Viosoft 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 import logging
16 import re
17 import time
18
19 from yardstick.benchmark.contexts.base import Context
20 from yardstick.common.process import check_if_process_failed
21 from yardstick.network_services import constants
22 from yardstick.network_services.helpers.cpu import CpuSysCores
23 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF
24 from yardstick.network_services.vnf_generic.vnf.vpp_helpers import \
25     VppSetupEnvHelper
26
27 LOG = logging.getLogger(__name__)
28
29
30 class VipsecApproxSetupEnvHelper(VppSetupEnvHelper):
31     DEFAULT_IPSEC_VNF_CFG = {
32         'crypto_type': 'SW_cryptodev',
33         'rxq': 1,
34         'worker_config': '1C/1T',
35         'worker_threads': 1,
36     }
37
38     def __init__(self, vnfd_helper, ssh_helper, scenario_helper):
39         super(VipsecApproxSetupEnvHelper, self).__init__(
40             vnfd_helper, ssh_helper, scenario_helper)
41
42     def _get_crypto_type(self):
43         vnf_cfg = self.scenario_helper.options.get('vnf_config',
44                                                    self.DEFAULT_IPSEC_VNF_CFG)
45         return vnf_cfg.get('crypto_type', 'SW_cryptodev')
46
47     def _get_crypto_algorithms(self):
48         vpp_cfg = self.scenario_helper.all_options.get('vpp_config', {})
49         return vpp_cfg.get('crypto_algorithms', 'aes-gcm')
50
51     def _get_n_tunnels(self):
52         vpp_cfg = self.scenario_helper.all_options.get('vpp_config', {})
53         return vpp_cfg.get('tunnels', 1)
54
55     def _get_n_connections(self):
56         try:
57             flow_cfg = self.scenario_helper.all_options['flow']
58             return flow_cfg['count']
59         except KeyError:
60             raise KeyError("Missing flow definition in scenario section" +
61                            " of the task definition file")
62
63     def _get_flow_src_start_ip(self):
64         node_name = self.find_encrypted_data_interface()["node_name"]
65         try:
66             flow_cfg = self.scenario_helper.all_options['flow']
67             src_ips = flow_cfg['src_ip']
68             dst_ips = flow_cfg['dst_ip']
69         except KeyError:
70             raise KeyError("Missing flow definition in scenario section" +
71                            " of the task definition file")
72
73         for src, dst in zip(src_ips, dst_ips):
74             flow_src_start_ip, _ = src.split('-')
75             flow_dst_start_ip, _ = dst.split('-')
76
77             if node_name == "vnf__0":
78                 return flow_src_start_ip
79             elif node_name == "vnf__1":
80                 return flow_dst_start_ip
81
82     def _get_flow_dst_start_ip(self):
83         node_name = self.find_encrypted_data_interface()["node_name"]
84         try:
85             flow_cfg = self.scenario_helper.all_options['flow']
86             src_ips = flow_cfg['src_ip']
87             dst_ips = flow_cfg['dst_ip']
88         except KeyError:
89             raise KeyError("Missing flow definition in scenario section" +
90                            " of the task definition file")
91
92         for src, dst in zip(src_ips, dst_ips):
93             flow_src_start_ip, _ = src.split('-')
94             flow_dst_start_ip, _ = dst.split('-')
95
96             if node_name == "vnf__0":
97                 return flow_dst_start_ip
98             elif node_name == "vnf__1":
99                 return flow_src_start_ip
100
101     def build_config(self):
102         # TODO Implement later
103         pass
104
105     def setup_vnf_environment(self):
106         resource = super(VipsecApproxSetupEnvHelper,
107                          self).setup_vnf_environment()
108
109         self.start_vpp_service()
110
111         sys_cores = CpuSysCores(self.ssh_helper)
112         self._update_vnfd_helper(sys_cores.get_cpu_layout())
113
114         return resource
115
116     @staticmethod
117     def calculate_frame_size(frame_cfg):
118         if not frame_cfg:
119             return 64
120
121         imix_count = {size.upper().replace('B', ''): int(weight)
122                       for size, weight in frame_cfg.items()}
123         imix_sum = sum(imix_count.values())
124         if imix_sum <= 0:
125             return 64
126         packets_total = sum([int(size) * weight
127                              for size, weight in imix_count.items()])
128         return packets_total / imix_sum
129
130     def check_status(self):
131         ipsec_created = False
132         cmd = "vppctl show int"
133         _, stdout, _ = self.ssh_helper.execute(cmd)
134         entries = re.split(r"\n+", stdout)
135         tmp = [re.split(r"\s\s+", entry, 5) for entry in entries]
136
137         for item in tmp:
138             if isinstance(item, list):
139                 if item[0] and item[0] != 'local0':
140                     if "ipsec" in item[0] and not ipsec_created:
141                         ipsec_created = True
142                     if len(item) > 2 and item[2] == 'down':
143                         return False
144         return ipsec_created
145
146     def get_vpp_statistics(self):
147         # TODO Implement later
148         return None
149
150     def create_ipsec_tunnels(self):
151         # TODO Implement later
152         pass
153
154     def find_raw_data_interface(self):
155         try:
156             return self.vnfd_helper.find_virtual_interface(vld_id="uplink_0")
157         except KeyError:
158             return self.vnfd_helper.find_virtual_interface(vld_id="downlink_0")
159
160     def find_encrypted_data_interface(self):
161         return self.vnfd_helper.find_virtual_interface(vld_id="ciphertext")
162
163
164 class VipsecApproxVnf(SampleVNF):
165     """ This class handles vIPSEC VNF model-driver definitions """
166
167     APP_NAME = 'vIPSEC'
168     APP_WORD = 'vipsec'
169     WAIT_TIME = 20
170
171     def __init__(self, name, vnfd, setup_env_helper_type=None,
172                  resource_helper_type=None):
173         if setup_env_helper_type is None:
174             setup_env_helper_type = VipsecApproxSetupEnvHelper
175         super(VipsecApproxVnf, self).__init__(
176             name, vnfd, setup_env_helper_type,
177             resource_helper_type)
178
179     def _run(self):
180         # we can't share ssh paramiko objects to force new connection
181         self.ssh_helper.drop_connection()
182         # kill before starting
183         self.setup_helper.kill_vnf()
184         self._build_config()
185         self.setup_helper.create_ipsec_tunnels()
186
187     def wait_for_instantiate(self):
188         time.sleep(self.WAIT_TIME)
189         while True:
190             status = self.setup_helper.check_status()
191             if not self._vnf_process.is_alive() and not status:
192                 raise RuntimeError("%s VNF process died." % self.APP_NAME)
193             LOG.info("Waiting for %s VNF to start.. ", self.APP_NAME)
194             time.sleep(self.WAIT_TIME_FOR_SCRIPT)
195             status = self.setup_helper.check_status()
196             if status:
197                 LOG.info("%s VNF is up and running.", self.APP_NAME)
198                 self._vnf_up_post()
199                 return self._vnf_process.exitcode
200
201     def terminate(self):
202         self.setup_helper.kill_vnf()
203         self._tear_down()
204         self.resource_helper.stop_collect()
205         if self._vnf_process is not None:
206             # be proper and join first before we kill
207             LOG.debug("joining before terminate %s", self._vnf_process.name)
208             self._vnf_process.join(constants.PROCESS_JOIN_TIMEOUT)
209             self._vnf_process.terminate()
210
211     def collect_kpi(self):
212         # we can't get KPIs if the VNF is down
213         check_if_process_failed(self._vnf_process, 0.01)
214         physical_node = Context.get_physical_node_from_server(
215             self.scenario_helper.nodes[self.name])
216         result = {"physical_node": physical_node}
217         result["collect_stats"] = self.setup_helper.get_vpp_statistics()
218         LOG.debug("%s collect KPIs %s", self.APP_NAME, result)
219         return result