2 ## Copyright (c) 2019 Intel Corporation
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
8 ## http://www.apache.org/licenses/LICENSE-2.0
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.
20 from kubernetes import client, config
22 from .rapid_sshclient import SSHClient
25 """Class which represents test pods.
26 For example with traffic gen, forward/swap applications, etc
33 _namespace = "default"
34 _nodeSelector_hostname = None
49 def __init__(self, name, pod_nodeport = None, namespace = "default",
50 logger_name = "k8srapid"):
51 self._log = logging.getLogger(logger_name)
54 self._namespace = namespace
55 self._ssh_client = SSHClient(logger_name = logger_name)
57 self._pod_nodeport = pod_nodeport
60 """Destroy POD. Do a cleanup.
62 if self._ssh_client is not None:
63 self._ssh_client.disconnect()
65 def create_from_yaml(self):
66 """Load POD description from yaml file.
68 with open(path.join(path.dirname(__file__),
69 self._spec_filename)) as yaml_file:
70 self.body = yaml.safe_load(yaml_file)
72 self.body["metadata"]["name"] = self._name
73 self.body["metadata"]["labels"] = {'app': self._pod_nodeport}
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)
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,
90 """Terminate POD. Close SSH connection.
92 if self._ssh_client is not None:
93 self._ssh_client.disconnect()
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))
102 def update_admin_ip(self):
103 """Check for admin IP address assigned by k8s.
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
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))
120 def wait_for_start(self):
121 """Wait for POD to start.
123 self._log.info("Waiting for POD %s to start..." % self._name)
126 if (self._last_status == "Running" or self._last_status == "Failed"
127 or self._last_status == "Unknown"):
132 self.update_admin_ip()
134 return self._last_status
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.
140 self._ssh_client.run_cmd(cmd)
145 def get_admin_ip(self):
146 return self._admin_ip
148 def get_admin_port(self):
149 return self._ssh_port
151 def get_socket_port(self):
152 return self._socket_port
157 def get_dp_subnet(self):
158 return self._dp_subnet
160 def get_dp_mac(self):
161 return self._sriov_vf_mac
163 def get_dp_pci_dev(self):
164 return self._sriov_vf
166 def get_qat_pci_dev(self):
172 def get_status(self):
173 """Get current status fro the pod.
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))
181 self._last_status = pod.status.phase
182 return self._last_status
184 def get_qat_dev(self):
185 """Get qat devices if any, assigned by k8s QAT device plugin.
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")
190 self._log.error("Failed to check assigned QAT VF!"
191 "Error %s" % self._ssh_client.get_error())
194 cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
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)
203 self._log.debug("No QAT devices for this pod")
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.
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")
214 self._log.error("Failed to check assigned SRIOV VF!"
215 "Error %s" % self._ssh_client.get_error())
218 cmd_output = self._ssh_client.get_output().decode("utf-8").rstrip()
219 self._log.debug("Environment variable %s" % cmd_output)
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)
227 self._log.info("Checking DPDK version for POD %s" % self._name)
228 ret = self._ssh_client.run_cmd("cat /opt/rapid/dpdk_version")
230 self._log.error("Failed to check DPDK version"
231 "Error %s" % self._ssh_client.get_error())
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'
238 allow_parameter = 'pci-whitelist'
240 self._log.info("Getting MAC address for assigned SRIOV VF %s" % \
242 self._ssh_client.run_cmd("sudo /opt/rapid/port_info_app -n 4 \
243 --{} {}".format(allow_parameter, self._sriov_vf))
245 self._log.error("Failed to get MAC address!"
246 "Error %s" % self._ssh_client.get_error())
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:]
257 self._log.debug("MAC %s" % self._sriov_vf_mac)
259 def set_dp_ip(self, dp_ip):
262 def set_dp_subnet(self, dp_subnet):
263 self._dp_subnet = dp_subnet
265 def set_id(self, pod_id):
268 def set_nodeselector(self, hostname):
269 """Set hostname on which POD will be executed.
271 self._nodeSelector_hostname = hostname
273 def set_spec_file_name(self, file_name):
274 """Set pod spec filename.
276 self._spec_filename = file_name
278 def set_ssh_credentials(self, user, rsa_private_key):
279 """Set SSH credentials for the SSH connection to the POD.
281 self.update_admin_ip()
282 self._ssh_client.set_credentials(ip = self._admin_ip,
284 rsa_private_key = rsa_private_key,
285 ssh_port = self._ssh_port)