Support packets in flight
[samplevnf.git] / VNFs / DPPD-PROX / helper-scripts / rapid / rapid_k8s_pod.py
1 ##
2 ## Copyright (c) 2019 Intel Corporation
3 ##
4 ## Licensed under the Apache License, Version 2.0 (the "License");
5 ## you may not use this file except in compliance with the License.
6 ## You may obtain a copy of the License at
7 ##
8 ##     http://www.apache.org/licenses/LICENSE-2.0
9 ##
10 ## Unless required by applicable law or agreed to in writing, software
11 ## distributed under the License is distributed on an "AS IS" BASIS,
12 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ## See the License for the specific language governing permissions and
14 ## limitations under the License.
15 ##
16
17 from os import path
18 import time, yaml
19 import logging
20 from kubernetes import client, config
21
22 from rapid_sshclient import SSHClient
23
24 class Pod:
25     """Class which represents test pods.
26     For example with traffic gen, forward/swap applications, etc
27     """
28     k8s_CoreV1Api = None
29
30     _log = None
31
32     _name = "pod"
33     _namespace = "default"
34     _nodeSelector_hostname = None
35     _spec_filename = None
36     _last_status = None
37     _id = None
38     _admin_ip = None
39     _dp_ip = None
40     _dp_subnet = None
41
42     _ssh_client = None
43
44     _sriov_vf = None
45     _sriov_vf_mac = None
46
47     def __init__(self, name, namespace = "default", logger_name = "k8srapid"):
48         self._log = logging.getLogger(logger_name)
49
50         self._name = name
51         self._namespace = namespace
52         self._ssh_client = SSHClient(logger_name = logger_name)
53         self.qat_vf = []
54
55     def __del__(self):
56         """Destroy POD. Do a cleanup.
57         """
58         if self._ssh_client is not None:
59             self._ssh_client.disconnect()
60
61     def create_from_yaml(self):
62         """Load POD description from yaml file.
63         """
64         with open(path.join(path.dirname(__file__),
65             self._spec_filename)) as yaml_file:
66             self.body = yaml.safe_load(yaml_file)
67
68             self.body["metadata"]["name"] = self._name
69
70             if (self._nodeSelector_hostname is not None):
71                 if ("nodeSelector" not in self.body["spec"]):
72                     self.body["spec"]["nodeSelector"] = {}
73                 self.body["spec"]["nodeSelector"]["kubernetes.io/hostname"] = \
74                         self._nodeSelector_hostname
75             self._log.debug("Creating POD, body:\n%s" % self.body)
76
77             try:
78                 self.k8s_CoreV1Api.create_namespaced_pod(body = self.body,
79                                                 namespace = self._namespace)
80             except client.rest.ApiException as e:
81                 self._log.error("Couldn't create POD %s!\n%s\n" % (self._name,
82                     e))
83
84     def terminate(self):
85         """Terminate POD. Close SSH connection.
86         """
87         if self._ssh_client is not None:
88             self._ssh_client.disconnect()
89
90         try:
91             self.k8s_CoreV1Api.delete_namespaced_pod(name = self._name,
92                                                      namespace = self._namespace)
93         except client.rest.ApiException as e:
94             if e.reason != "Not Found":
95                 self._log.error("Couldn't delete POD %s!\n%s\n" % (self._name, e.reason))
96
97     def update_admin_ip(self):
98         """Check for admin IP address assigned by k8s.
99         """
100         try:
101             pod = self.k8s_CoreV1Api.read_namespaced_pod_status(name = self._name, namespace = self._namespace)
102             self._admin_ip = pod.status.pod_ip
103         except client.rest.ApiException as e:
104             self._log.error("Couldn't update POD %s admin IP!\n%s\n" % (self._name, e))
105
106     def wait_for_start(self):
107         """Wait for POD to start.
108         """
109         self._log.info("Waiting for POD %s to start..." % self._name)
110         while True:
111             self.get_status()
112             if (self._last_status == "Running" or self._last_status == "Failed"
113                 or self._last_status == "Unknown"):
114                 break
115             else:
116                 time.sleep(3)
117
118         self.update_admin_ip()
119
120         return self._last_status
121
122     def ssh_run_cmd(self, cmd):
123         """Execute command for POD via SSH connection.
124         SSH credentials should be configured before use of this function.
125         """
126         self._ssh_client.run_cmd(cmd)
127
128     def get_name(self):
129         return self._name
130
131     def get_admin_ip(self):
132         return self._admin_ip
133
134     def get_dp_ip(self):
135         return self._dp_ip
136
137     def get_dp_subnet(self):
138         return self._dp_subnet
139
140     def get_dp_mac(self):
141         return self._sriov_vf_mac
142
143     def get_dp_pci_dev(self):
144         return self._sriov_vf
145
146     def get_qat_pci_dev(self):
147         return self.qat_vf
148
149     def get_id(self):
150         return self._id
151
152     def get_status(self):
153         """Get current status fro the pod.
154         """
155         try:
156             pod = self.k8s_CoreV1Api.read_namespaced_pod_status(name = self._name,
157                                                                 namespace = self._namespace)
158         except client.rest.ApiException as e:
159             self._log.error("Couldn't read POD %s status!\n%s\n" % (self._name, e))
160
161         self._last_status = pod.status.phase
162         return self._last_status
163
164     def get_qat_dev(self):
165         """Get qat devices if any, assigned by k8s QAT device plugin.
166         """
167         self._log.info("Checking assigned QAT VF for POD %s" % self._name)
168         ret = self._ssh_client.run_cmd("cat /opt/rapid/k8s_qat_device_plugin_envs")
169         if ret != 0:
170             self._log.error("Failed to check assigned QAT VF!"
171                             "Error %s" % self._ssh_client.get_error())
172             return -1
173
174         cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
175
176         if cmd_output:
177             self._log.debug("Before: Using QAT VF %s" % self.qat_vf)
178             self._log.debug("Environment variable %s" % cmd_output)
179             for line in cmd_output.splitlines():
180                 self.qat_vf.append(line.split("=")[1])
181             self._log.debug("Using QAT VF %s" % self.qat_vf)
182         else:
183             self._log.debug("No QAT devices for this pod")
184             self.qat_vf = None
185
186     def get_sriov_dev_mac(self):
187         """Get assigned by k8s SRIOV network device plugin SRIOV VF devices.
188         Return 0 in case of sucessfull configuration.
189         Otherwise return -1.
190         """
191         self._log.info("Checking assigned SRIOV VF for POD %s" % self._name)
192         ret = self._ssh_client.run_cmd("cat /opt/rapid/k8s_sriov_device_plugin_envs")
193         if ret != 0:
194             self._log.error("Failed to check assigned SRIOV VF!"
195                             "Error %s" % self._ssh_client.get_error())
196             return -1
197
198         cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
199         self._log.debug("Environment variable %s" % cmd_output)
200
201         # Parse environment variable
202         cmd_output = cmd_output.split("=")[1]
203         self._sriov_vf = cmd_output.split(",")[0]
204         self._log.debug("Using first SRIOV VF %s" % self._sriov_vf)
205
206         # find DPDK version
207         self._log.info("Checking DPDK version for POD %s" % self._name)
208         ret = self._ssh_client.run_cmd("cat /opt/rapid/dpdk_version")
209         if ret != 0:
210             self._log.error("Failed to check DPDK version"
211                             "Error %s" % self._ssh_client.get_error())
212             return -1
213         dpdk_version = self._ssh_client.get_output().decode("utf-8").rstrip()
214         self._log.debug("DPDK version %s" % dpdk_version)
215         if (dpdk_version >= '20.11.0'):
216             allow_parameter = 'allow'
217         else:
218             allow_parameter = 'pci-whitelist'
219
220         self._log.info("Getting MAC address for assigned SRIOV VF %s" % \
221                 self._sriov_vf)
222         self._ssh_client.run_cmd("sudo /opt/rapid/port_info_app -n 4 \
223                 --{} {}".format(allow_parameter, self._sriov_vf))
224         if ret != 0:
225             self._log.error("Failed to get MAC address!"
226                             "Error %s" % self._ssh_client.get_error())
227             return -1
228
229         # Parse MAC address
230         cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
231         self._log.debug(cmd_output)
232         cmd_output = cmd_output.splitlines()
233         for line in cmd_output:
234             if line.startswith("Port 0 MAC: "):
235                 self._sriov_vf_mac = line[12:]
236
237         self._log.debug("MAC %s" % self._sriov_vf_mac)
238
239     def set_dp_ip(self, dp_ip):
240         self._dp_ip = dp_ip
241
242     def set_dp_subnet(self, dp_subnet):
243         self._dp_subnet = dp_subnet
244
245     def set_id(self, pod_id):
246         self._id = pod_id
247
248     def set_nodeselector(self, hostname):
249         """Set hostname on which POD will be executed.
250         """
251         self._nodeSelector_hostname = hostname
252
253     def set_spec_file_name(self, file_name):
254         """Set pod spec filename.
255         """
256         self._spec_filename = file_name
257
258     def set_ssh_credentials(self, user, rsa_private_key):
259         """Set SSH credentials for the SSH connection to the POD.
260         """
261         self.update_admin_ip()
262         self._ssh_client.set_credentials(ip = self._admin_ip,
263                                          user = user,
264                                          rsa_private_key = rsa_private_key)