specify kubeconfig file
[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     _ssh_port = 22
47     _socket_port = 8474
48
49     def __init__(self, name, pod_nodeport = None, namespace = "default",
50             logger_name = "k8srapid"):
51         self._log = logging.getLogger(logger_name)
52
53         self._name = name
54         self._namespace = namespace
55         self._ssh_client = SSHClient(logger_name = logger_name)
56         self.qat_vf = []
57         self._pod_nodeport = pod_nodeport
58
59     def __del__(self):
60         """Destroy POD. Do a cleanup.
61         """
62         if self._ssh_client is not None:
63             self._ssh_client.disconnect()
64
65     def create_from_yaml(self):
66         """Load POD description from yaml file.
67         """
68         with open(path.join(path.dirname(__file__),
69             self._spec_filename)) as yaml_file:
70             self.body = yaml.safe_load(yaml_file)
71
72             self.body["metadata"]["name"] = self._name
73             self.body["metadata"]["labels"] = {'app': self._pod_nodeport}
74
75             if (self._nodeSelector_hostname is not None):
76                 if ("nodeSelector" not in self.body["spec"]):
77                     self.body["spec"]["nodeSelector"] = {}
78                 self.body["spec"]["nodeSelector"]["kubernetes.io/hostname"] = \
79                         self._nodeSelector_hostname
80             self._log.debug("Creating POD, body:\n%s" % self.body)
81
82             try:
83                 self.k8s_CoreV1Api.create_namespaced_pod(body = self.body,
84                                                 namespace = self._namespace)
85             except client.rest.ApiException as e:
86                 self._log.error("Couldn't create POD %s!\n%s\n" % (self._name,
87                     e))
88
89     def terminate(self):
90         """Terminate POD. Close SSH connection.
91         """
92         if self._ssh_client is not None:
93             self._ssh_client.disconnect()
94
95         try:
96             self.k8s_CoreV1Api.delete_namespaced_pod(name = self._name,
97                                                      namespace = self._namespace)
98         except client.rest.ApiException as e:
99             if e.reason != "Not Found":
100                 self._log.error("Couldn't delete POD %s!\n%s\n" % (self._name, e.reason))
101
102     def update_admin_ip(self):
103         """Check for admin IP address assigned by k8s.
104         """
105         try:
106             if self._pod_nodeport:
107                 service= self.k8s_CoreV1Api.read_namespaced_service_status(name = self._pod_nodeport, namespace = self._namespace)
108                 self._admin_ip = service.spec.cluster_ip
109                 for service_port in service.spec.ports:
110                     if service_port.name == 'control-port':
111                         self._ssh_port = service_port.node_port
112                     if service_port.name == 'socket-port':
113                         self._socket_port = service_port.node_port
114             else:
115                 pod = self.k8s_CoreV1Api.read_namespaced_pod_status(name = self._name, namespace = self._namespace)
116                 self._admin_ip = pod.status.pod_ip
117         except client.rest.ApiException as e:
118             self._log.error("Couldn't update POD %s admin IP!\n%s\n" % (self._name, e))
119
120     def wait_for_start(self):
121         """Wait for POD to start.
122         """
123         self._log.info("Waiting for POD %s to start..." % self._name)
124         while True:
125             self.get_status()
126             if (self._last_status == "Running" or self._last_status == "Failed"
127                 or self._last_status == "Unknown"):
128                 break
129             else:
130                 time.sleep(3)
131
132         self.update_admin_ip()
133
134         return self._last_status
135
136     def ssh_run_cmd(self, cmd):
137         """Execute command for POD via SSH connection.
138         SSH credentials should be configured before use of this function.
139         """
140         self._ssh_client.run_cmd(cmd)
141
142     def get_name(self):
143         return self._name
144
145     def get_admin_ip(self):
146         return self._admin_ip
147
148     def get_admin_port(self):
149         return self._ssh_port
150
151     def get_socket_port(self):
152         return self._socket_port
153
154     def get_dp_ip(self):
155         return self._dp_ip
156
157     def get_dp_subnet(self):
158         return self._dp_subnet
159
160     def get_dp_mac(self):
161         return self._sriov_vf_mac
162
163     def get_dp_pci_dev(self):
164         return self._sriov_vf
165
166     def get_qat_pci_dev(self):
167         return self.qat_vf
168
169     def get_id(self):
170         return self._id
171
172     def get_status(self):
173         """Get current status fro the pod.
174         """
175         try:
176             pod = self.k8s_CoreV1Api.read_namespaced_pod_status(name = self._name,
177                                                                 namespace = self._namespace)
178         except client.rest.ApiException as e:
179             self._log.error("Couldn't read POD %s status!\n%s\n" % (self._name, e))
180
181         self._last_status = pod.status.phase
182         return self._last_status
183
184     def get_qat_dev(self):
185         """Get qat devices if any, assigned by k8s QAT device plugin.
186         """
187         self._log.info("Checking assigned QAT VF for POD %s" % self._name)
188         ret = self._ssh_client.run_cmd("cat /opt/rapid/k8s_qat_device_plugin_envs")
189         if ret != 0:
190             self._log.error("Failed to check assigned QAT VF!"
191                             "Error %s" % self._ssh_client.get_error())
192             return -1
193
194         cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
195
196         if cmd_output:
197             self._log.debug("Before: Using QAT VF %s" % self.qat_vf)
198             self._log.debug("Environment variable %s" % cmd_output)
199             for line in cmd_output.splitlines():
200                 self.qat_vf.append(line.split("=")[1])
201             self._log.debug("Using QAT VF %s" % self.qat_vf)
202         else:
203             self._log.debug("No QAT devices for this pod")
204             self.qat_vf = None
205
206     def get_sriov_dev_mac(self):
207         """Get assigned by k8s SRIOV network device plugin SRIOV VF devices.
208         Return 0 in case of sucessfull configuration.
209         Otherwise return -1.
210         """
211         self._log.info("Checking assigned SRIOV VF for POD %s" % self._name)
212         ret = self._ssh_client.run_cmd("cat /opt/rapid/k8s_sriov_device_plugin_envs")
213         if ret != 0:
214             self._log.error("Failed to check assigned SRIOV VF!"
215                             "Error %s" % self._ssh_client.get_error())
216             return -1
217
218         cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
219         self._log.debug("Environment variable %s" % cmd_output)
220
221         # Parse environment variable
222         cmd_output = cmd_output.split("=")[1]
223         self._sriov_vf = cmd_output.split(",")[0]
224         self._log.debug("Using first SRIOV VF %s" % self._sriov_vf)
225
226         # find DPDK version
227         self._log.info("Checking DPDK version for POD %s" % self._name)
228         ret = self._ssh_client.run_cmd("cat /opt/rapid/dpdk_version")
229         if ret != 0:
230             self._log.error("Failed to check DPDK version"
231                             "Error %s" % self._ssh_client.get_error())
232             return -1
233         dpdk_version = self._ssh_client.get_output().decode("utf-8").rstrip()
234         self._log.debug("DPDK version %s" % dpdk_version)
235         if (dpdk_version >= '20.11.0'):
236             allow_parameter = 'allow'
237         else:
238             allow_parameter = 'pci-whitelist'
239
240         self._log.info("Getting MAC address for assigned SRIOV VF %s" % \
241                 self._sriov_vf)
242         self._ssh_client.run_cmd("sudo /opt/rapid/port_info_app -n 4 \
243                 --{} {}".format(allow_parameter, self._sriov_vf))
244         if ret != 0:
245             self._log.error("Failed to get MAC address!"
246                             "Error %s" % self._ssh_client.get_error())
247             return -1
248
249         # Parse MAC address
250         cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
251         self._log.debug(cmd_output)
252         cmd_output = cmd_output.splitlines()
253         for line in cmd_output:
254             if line.startswith("Port 0 MAC: "):
255                 self._sriov_vf_mac = line[12:]
256
257         self._log.debug("MAC %s" % self._sriov_vf_mac)
258
259     def set_dp_ip(self, dp_ip):
260         self._dp_ip = dp_ip
261
262     def set_dp_subnet(self, dp_subnet):
263         self._dp_subnet = dp_subnet
264
265     def set_id(self, pod_id):
266         self._id = pod_id
267
268     def set_nodeselector(self, hostname):
269         """Set hostname on which POD will be executed.
270         """
271         self._nodeSelector_hostname = hostname
272
273     def set_spec_file_name(self, file_name):
274         """Set pod spec filename.
275         """
276         self._spec_filename = file_name
277
278     def set_ssh_credentials(self, user, rsa_private_key):
279         """Set SSH credentials for the SSH connection to the POD.
280         """
281         self.update_admin_ip()
282         self._ssh_client.set_credentials(ip = self._admin_ip,
283                                          user = user,
284                                          rsa_private_key = rsa_private_key,
285                                          ssh_port = self._ssh_port)