Source code for lib.polycube

from json import loads

from falcon.errors import HTTPGatewayTimeout as HTTP_Gateway_Timeout
from falcon.errors import HTTPServiceUnavailable as HTTP_Service_Unavailable
from requests import HTTPError as HTTP_Error
from requests import delete as delete_req
from requests import get as get_req
from requests import post as post_req
from requests import put as put_req
from requests.exceptions import ConnectionError, Timeout

from reader.arg import ArgReader
from utils.log import Log

MSG_CONN_NOT_POSSIBLE = "Connection to Polycube at {} not possible"
MSG_RESP_NOT_RECV = "Timely response not received from polycube at {} in {} seconds."  # noqa: E501


[docs]class Polycube: def __init__(self): self.host = ArgReader.db.polycube_host self.port = ArgReader.db.polycube_port self.timeout = ArgReader.db.polycube_timeout self.endpoint = f"http://{self.host}:{self.port}/polycube/v1" self.log = Log.get("polycube") self.log.info(f"Check connection to {self.endpoint}") try: resp_req = get_req( self.endpoint, timeout=ArgReader.db.polycube_timeout ) self.__manager(resp_req) except (ConnectionError, HTTP_Error) as e: self.log.exception( f"""Connection with polycube at {self.host}:{self.port} not possible""", e, ) def get(self, cube): self.log.info(f"Get info of cube {cube}") try: resp_req = get_req( f"{self.endpoint}/dynmon/{cube}", timeout=self.timeout ) self.__manager(resp_req) return loads(resp_req.content) except HTTP_Error: return None def create(self, cube, code, interface, metrics): data = { "name": cube, "code": code, "interface": interface, "metrics": metrics, } if self.get(cube) is None: self.log.info(f"Create cube {cube}.") attached_info = {} try: dp_cfg = self.__dataplane_config(cube, code, metrics) resp_req = put_req( f"{self.endpoint}/dynmon/{cube}", json={"dataplane-config": dp_cfg}, timeout=self.timeout, ) attached_info = self.__attach(cube, interface) return dict( status="created", attached_info=attached_info, detached_info={}, data=data, **self.__from_resp(resp_req), ) except Exception as e: self.log.exception(f"Cube {cube} not created", e) return dict( status="error", interface=attached_info, detached_info={}, data=data, **self.__from_resp(resp_req, error=True), ) else: return { "error": True, "description": f"Cube {cube} found.", "data": data, } def delete(self, cube): data = {"cube": cube} if self.get(cube) is None: return { "error": True, "description": f"Cube {cube} not found.", "data": data, } self.log.info(f"Delete cube {cube}.") try: resp_req = delete_req( f"{self.endpoint}/dynmon/{cube}", timeout=self.timeout ) self.__manager(resp_req) return dict( status="deleted", data=data, **self.__from_resp(resp_req) ) except Exception as e: self.log.exception(f"Cube {cube} not deleted", e) return dict( error=True, data=data, **self.__from_resp(resp_req, error=True) ) def update(self, cube, code, interface, metrics): data = { "name": cube, "code": code, "interface": interface, "metrics": metrics, } service = self.get(cube) if service is None: return { "error": True, "description": f"Cube {cube} not found.", "data": data, } self.log.info(f"Update cube {cube}.") try: attached_iface = service.get("parent", None) attached_info = {} detached_info = {} if attached_iface is None: attached_info = self.__attach(cube, cube) elif attached_iface != interface: attached_info = self.__detach(cube, attached_iface) detached_info = self.__attach(cube, interface) req_path = "dynmon/{cube}/dataplane-config" resp_req = put_req( f"{self.endpoint}/{req_path}", json=self.__dataplane_config(cube, code, metrics), timeout=self.timeout, ) self.__manager(resp_req) return dict( status="updated", attached_info=attached_info, detached_info=detached_info, data=data, **self.__from_resp(resp_req), ) except Exception as e: self.log.exception(f"Cube {cube} not updated", e) return dict( status="error", attached_info=attached_info, detached_info=detached_info, data=data, **self.__from_resp(resp_req, error=True), ) @staticmethod def __dataplane_config(cube, code, metrics): return { "ingress-path": { "name": cube, "code": code, "metric-configs": metrics, }, "egress-path": {}, } def __from_resp(self, resp, error=None): error = error if error is not None else resp.status_code >= 400 if not resp.content: return {"error": error} try: return loads(resp.content) except Exception: return {"error": error, "message": resp.content.decode("utf-8")} def __detach(self, cube, interface): return self.__extracted_from___attach_2( "/detach", cube, interface, "detached" ) def __attach(self, cube, interface): return self.__extracted_from___attach_2( "/attach", cube, interface, "attached" ) # TODO Rename this here and in `__detach` and `__attach` def __extracted_from___attach_2(self, arg0, cube, interface, status): resp_req = post_req( f"{self.endpoint}{arg0}", json={"cube": cube, "port": interface}, timeout=self.timeout, ) self.__manager(resp_req) return dict( status=status, data={"cube": cube, "interface": interface}, **self.__from_resp(resp_req), ) def __manager(self, resp_req): try: resp_req.raise_for_status() except ConnectionError as e: _msg = MSG_CONN_NOT_POSSIBLE.format(self.endpoint) self.log.exception(_msg, e) if resp_req.content: self.log.error(f"Response: {resp_req.content}") raise HTTP_Service_Unavailable( title="Connection error", description=_msg ) except Timeout as e: timeout = ArgReader.db.polycube_timeout _msg = MSG_RESP_NOT_RECV.format(self.endpoint, timeout) self.log.exception(_msg, e) if resp_req.content: self.log.error(f"Response: {resp_req.content}") raise HTTP_Gateway_Timeout( title="Polycube Unavailable", description=_msg )