From 2a1bada8fbb22b8dfb6c621d1c4c45e19e7ef0dc Mon Sep 17 00:00:00 2001 From: Rogerio Motitsuki Date: Thu, 18 Apr 2019 20:19:01 -0300 Subject: [PATCH 1/6] Add API calls to install/uninstall/enable/disable napps --- kytos/core/api_server.py | 119 ++++++++++++++++++++++++++++++++++++--- kytos/core/controller.py | 12 ++-- 2 files changed, 118 insertions(+), 13 deletions(-) diff --git a/kytos/core/api_server.py b/kytos/core/api_server.py index 11a7ea2ce..00e87a26e 100644 --- a/kytos/core/api_server.py +++ b/kytos/core/api_server.py @@ -8,6 +8,7 @@ import zipfile from datetime import datetime from glob import glob +from http import HTTPStatus from urllib.error import HTTPError, URLError from urllib.request import urlopen, urlretrieve @@ -25,16 +26,21 @@ class APIServer: _CORE_PREFIX = "/api/kytos/core/" def __init__(self, app_name, listen='0.0.0.0', port=8181, - napps_dir=None): + controller=None): """Start a Flask+SocketIO server. + Require controller to get NApps dir and NAppsManager + Args: app_name(string): String representing a App Name listen (string): host name used by api server instance port (int): Port number used by api server instance - napps_dir(string): napps path directory + controller(kytos.core.controller): A controller instance. """ dirname = os.path.dirname(os.path.abspath(__file__)) + self.nappsManager = controller.napps_manager + self.napps_dir = controller.options.napps + self.flask_dir = os.path.join(dirname, '../web-ui') self.log = logging.getLogger('api_server') @@ -53,7 +59,6 @@ def __init__(self, app_name, listen='0.0.0.0', port=8181, # Update web-ui if necessary self.update_web_ui(force=False) - self.napps_dir = napps_dir def _enable_websocket_rooms(self): socket = self.server @@ -90,6 +95,9 @@ def start_api(self): self.register_core_endpoint('web/update/', self.update_web_ui, methods=['POST']) + + self.register_core_napp_services() + self._register_web_ui() def register_core_endpoint(self, rule, function, **options): @@ -112,7 +120,7 @@ def _register_web_ui(self): @staticmethod def status_api(): """Display kytos status using the route ``/kytos/status/``.""" - return '{"response": "running"}', 201 + return '{"response": "running"}', HTTPStatus.CREATED.value def stop_api_server(self): """Send a shutdown request to stop Api Server.""" @@ -131,18 +139,18 @@ def shutdown_api(self): allowed_host = ['127.0.0.1:'+str(self.port), 'localhost:'+str(self.port)] if request.host not in allowed_host: - return "", 403 + return "", HTTPStatus.FORBIDDEN.value self.server.stop() - return 'Server shutting down...', 200 + return 'Server shutting down...', HTTPStatus.OK.value def static_web_ui(self, username, napp_name, filename): """Serve static files from installed napps.""" path = f"{self.napps_dir}/{username}/{napp_name}/ui/{filename}" if os.path.exists(path): return send_file(path) - return "", 404 + return "", HTTPStatus.NOT_FOUND.value def get_ui_components(self, section_name): """Return all napps ui components from an specific section. @@ -332,3 +340,100 @@ def remove_napp_endpoints(self, napp): # pylint: enable=protected-access self.log.info('The Rest endpoints from %s were disabled.', prefix) + + def register_core_napp_services(self): + """ + Register /kytos/core/ services over NApps. + + It registers enable, disable, install, uninstall NApps that will + be used by kytos-utils. + """ + self.register_core_endpoint("napps///enable", + self._enable_napp) + + self.register_core_endpoint("napps///disable", + self._disable_napp) + + self.register_core_endpoint("napps///install", + self._install_napp) + self.register_core_endpoint("napps///uninstall", + self._uninstall_napp) + + def _enable_napp(self, username, napp_name): + """ + Enable an installed NApp. + + :param username: NApps user name + :param napp_name: NApp name + :return: JSON content and return code + """ + # Check if the NApp is installed + if not self.nappsManager.is_installed(username, napp_name): + return '{"response": "not installed"}', \ + HTTPStatus.BAD_REQUEST.value + + # Check if the NApp is already been enabled + if not self.nappsManager.is_enabled(username, napp_name): + self.nappsManager.enable(username, napp_name) + + # Check if NApp is enabled + if self.nappsManager.is_enabled(username, napp_name): + return '{"response": "enabled"}', HTTPStatus.OK.value + + # If it is not enabled an admin user must check the log file + return '{"response": "error"}', \ + HTTPStatus.INTERNAL_SERVER_ERROR.value + + def _disable_napp(self, username, napp_name): + """ + Disable an installed NApp. + + :param username: NApps user name + :param napp_name: NApp name + :return: JSON content and return code + """ + # Check if the NApp is installed + if not self.nappsManager.is_installed(username, napp_name): + return '{"response": "not installed"}', \ + HTTPStatus.BAD_REQUEST.value + + # Check if the NApp is enabled + if self.nappsManager.is_enabled(username, napp_name): + self.nappsManager.disable(username, napp_name) + + # Check if NApp is still enabled + if self.nappsManager.is_enabled(username, napp_name): + # If it is still enabled an admin user must check the log file + return '{"response": "error"}', \ + HTTPStatus.INTERNAL_SERVER_ERROR.value + + return '{"response": "disabled"}', \ + HTTPStatus.OK.value + + def _install_napp(self, username, napp_name): + # Check if the NApp is installed + if self.nappsManager.is_installed(username, napp_name): + return '{"response": "installed"}', HTTPStatus.OK.value + + napp = "{}/{}".format(username, napp_name) + + # Try to install the napp + if not self.nappsManager.install(napp, False): + # If it is not installed an admin user must check the log file + return '{"response": "error"}', \ + HTTPStatus.INTERNAL_SERVER_ERROR.value + + return '{"response": "installed"}', HTTPStatus.OK.value + + def _uninstall_napp(self, username, napp_name): + # Check if the NApp is installed + if not self.nappsManager.is_installed(username, napp_name): + return '{"response": "uninstalled"}', HTTPStatus.OK.value + + # Try to unload/uninstall the napp + if not self.nappsManager.uninstall(username, napp_name): + # If it is not uninstalled admin user must check the log file + return '{"response": "error"}', \ + HTTPStatus.INTERNAL_SERVER_ERROR.value + + return '{"response": "uninstalled"}', HTTPStatus.OK.value \ No newline at end of file diff --git a/kytos/core/controller.py b/kytos/core/controller.py index 9eb03851e..06d732a0b 100644 --- a/kytos/core/controller.py +++ b/kytos/core/controller.py @@ -110,18 +110,18 @@ def __init__(self, options=None, loop=None): #: logging.Logger: Logger instance used by Kytos. self.log = None - #: API Server used to expose rest endpoints. - self.api_server = APIServer(__name__, self.options.listen, - self.options.api_port, - napps_dir=self.options.napps) - - self._register_endpoints() #: Observer that handle NApps when they are enabled or disabled. self.napp_dir_listener = NAppDirListener(self) self.napps_manager = NAppsManager(self) + #: API Server used to expose rest endpoints. + self.api_server = APIServer(__name__, self.options.listen, + self.options.api_port, + controller=self) + + self._register_endpoints() #: Adding the napps 'enabled' directory into the PATH #: Now you can access the enabled napps with: #: from napps.. import ?.... From 926ae629d14e04ee57238f471a63d3c58c3e2f79 Mon Sep 17 00:00:00 2001 From: Rogerio Motitsuki Date: Mon, 13 May 2019 14:05:34 -0300 Subject: [PATCH 2/6] New Kytos core services to enable/disable NApps --- kytos/core/api_server.py | 55 ++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/kytos/core/api_server.py b/kytos/core/api_server.py index 00e87a26e..932155130 100644 --- a/kytos/core/api_server.py +++ b/kytos/core/api_server.py @@ -26,7 +26,7 @@ class APIServer: _CORE_PREFIX = "/api/kytos/core/" def __init__(self, app_name, listen='0.0.0.0', port=8181, - controller=None): + napps_manager=None, napps_dir=None): """Start a Flask+SocketIO server. Require controller to get NApps dir and NAppsManager @@ -38,8 +38,8 @@ def __init__(self, app_name, listen='0.0.0.0', port=8181, controller(kytos.core.controller): A controller instance. """ dirname = os.path.dirname(os.path.abspath(__file__)) - self.nappsManager = controller.napps_manager - self.napps_dir = controller.options.napps + self.nappsManager = napps_manager + self.napps_dir = napps_dir self.flask_dir = os.path.join(dirname, '../web-ui') self.log = logging.getLogger('api_server') @@ -350,14 +350,16 @@ def register_core_napp_services(self): """ self.register_core_endpoint("napps///enable", self._enable_napp) - self.register_core_endpoint("napps///disable", self._disable_napp) - self.register_core_endpoint("napps///install", self._install_napp) self.register_core_endpoint("napps///uninstall", self._uninstall_napp) + self.register_core_endpoint("napps_enabled", + self._list_enabled_napps) + self.register_core_endpoint("napps_installed", + self._list_installed_napps) def _enable_napp(self, username, napp_name): """ @@ -377,12 +379,12 @@ def _enable_napp(self, username, napp_name): self.nappsManager.enable(username, napp_name) # Check if NApp is enabled - if self.nappsManager.is_enabled(username, napp_name): - return '{"response": "enabled"}', HTTPStatus.OK.value + if not self.nappsManager.is_enabled(username, napp_name): + # If it is not enabled an admin user must check the log file + return '{"response": "error"}', \ + HTTPStatus.INTERNAL_SERVER_ERROR.value - # If it is not enabled an admin user must check the log file - return '{"response": "error"}', \ - HTTPStatus.INTERNAL_SERVER_ERROR.value + return '{"response": "enabled"}', HTTPStatus.OK.value def _disable_napp(self, username, napp_name): """ @@ -418,7 +420,7 @@ def _install_napp(self, username, napp_name): napp = "{}/{}".format(username, napp_name) # Try to install the napp - if not self.nappsManager.install(napp, False): + if not self.nappsManager.install(napp, enable=False): # If it is not installed an admin user must check the log file return '{"response": "error"}', \ HTTPStatus.INTERNAL_SERVER_ERROR.value @@ -427,13 +429,28 @@ def _install_napp(self, username, napp_name): def _uninstall_napp(self, username, napp_name): # Check if the NApp is installed - if not self.nappsManager.is_installed(username, napp_name): - return '{"response": "uninstalled"}', HTTPStatus.OK.value + if self.nappsManager.is_installed(username, napp_name): + # Try to unload/uninstall the napp + if not self.nappsManager.uninstall(username, napp_name): + # If it is not uninstalled admin user must check the log file + return '{"response": "error"}', \ + HTTPStatus.INTERNAL_SERVER_ERROR.value - # Try to unload/uninstall the napp - if not self.nappsManager.uninstall(username, napp_name): - # If it is not uninstalled admin user must check the log file - return '{"response": "error"}', \ - HTTPStatus.INTERNAL_SERVER_ERROR.value + return '{"response": "uninstalled"}', HTTPStatus.OK.value + + def _list_enabled_napps(self): + """Sorted list of (username, napp_name) of enabled napps.""" + serialized_dict = json.dumps( + self.nappsManager.get_enabled_napps(), + default=lambda a: [a.username, a.name]) + + return '{"napps": %s}' % serialized_dict, HTTPStatus.OK.value + + def _list_installed_napps(self): + """Sorted list of (username, napp_name) of installed napps.""" + serialized_dict = json.dumps( + self.nappsManager.get_installed_napps(), + default=lambda a: [a.username, a.name]) + + return '{"napps": %s}' % serialized_dict, HTTPStatus.OK.value - return '{"response": "uninstalled"}', HTTPStatus.OK.value \ No newline at end of file From 617353c4217ed328b7fbdb65700f7fe135be83c7 Mon Sep 17 00:00:00 2001 From: Rogerio Motitsuki Date: Mon, 13 May 2019 14:06:07 -0300 Subject: [PATCH 3/6] New Kytos core service to get NApp metadata --- kytos/core/api_server.py | 22 ++++++++++++++++++++++ kytos/core/napps/manager.py | 24 +++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/kytos/core/api_server.py b/kytos/core/api_server.py index 932155130..baa8fc8d0 100644 --- a/kytos/core/api_server.py +++ b/kytos/core/api_server.py @@ -360,6 +360,9 @@ def register_core_napp_services(self): self._list_enabled_napps) self.register_core_endpoint("napps_installed", self._list_installed_napps) + self.register_core_endpoint( + "napps///metadata/", + self._get_napp_metadata) def _enable_napp(self, username, napp_name): """ @@ -454,3 +457,22 @@ def _list_installed_napps(self): return '{"napps": %s}' % serialized_dict, HTTPStatus.OK.value + def _get_napp_metadata(self, username, napp_name, key): + """Get NApp metadata value. + + For safety reasons, only some keys can be retrieved: + napp_dependencies, description, version. + + """ + _VALID_KEYS = ['napp_dependencies', 'description', 'version'] + + if not self.nappsManager.is_installed(username, napp_name): + return "Napp is not installed.", HTTPStatus.BAD_REQUEST.value + + if key not in _VALID_KEYS: + return "Invalid key.", HTTPStatus.BAD_REQUEST.value + + data = self.nappsManager.get_napp_metadata(username, napp_name, key) + serialized_dict = json.dumps({key: data}) + + return '%s' % serialized_dict, HTTPStatus.OK.value diff --git a/kytos/core/napps/manager.py b/kytos/core/napps/manager.py index b6adf3419..e4274650b 100644 --- a/kytos/core/napps/manager.py +++ b/kytos/core/napps/manager.py @@ -211,6 +211,28 @@ def get_installed_napps(self): """Return all NApps installed on this controller FS.""" return self.get_napps_from_path(self._installed_path) + def get_napp_metadata(self, username, napp_name, key): + """Return a value from kytos.json. + + Args: + username (string): A Username. + napp_name (string): A NApp name + key (string): Key used to get the value within kytos.json. + + Returns: + meta (object): Value stored in kytos.json. + + """ + napp_id = "{}/{}".format(username, napp_name) + kytos_json = self._installed_path / napp_id / 'kytos.json' + try: + with kytos_json.open() as file_descriptor: + meta = json.load(file_descriptor) + return meta[key] + except (FileNotFoundError, json.JSONDecodeError, KeyError): + LOG.warning("Load napp metada failed: %s/kytos.json", napp_id) + return '' + @staticmethod def get_napps_from_path(path: Path): """List all NApps found in ``napps_dir``.""" @@ -233,7 +255,7 @@ def _create_module(path: Path): (path / '__init__.py').touch() @staticmethod - def _find_napp(napp, root: Path=None) -> Path: + def _find_napp(napp, root: Path = None) -> Path: """Return local NApp root folder. Search for kytos.json in _./_ folder and _./user/napp_. From e9aba686d896b24995430ac385903fd8316cd6e1 Mon Sep 17 00:00:00 2001 From: Rogerio Motitsuki Date: Tue, 21 May 2019 18:20:57 -0300 Subject: [PATCH 4/6] Fix class APIServer constructor --- kytos/core/controller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kytos/core/controller.py b/kytos/core/controller.py index 06d732a0b..ebb5df79f 100644 --- a/kytos/core/controller.py +++ b/kytos/core/controller.py @@ -110,7 +110,6 @@ def __init__(self, options=None, loop=None): #: logging.Logger: Logger instance used by Kytos. self.log = None - #: Observer that handle NApps when they are enabled or disabled. self.napp_dir_listener = NAppDirListener(self) @@ -119,7 +118,7 @@ def __init__(self, options=None, loop=None): #: API Server used to expose rest endpoints. self.api_server = APIServer(__name__, self.options.listen, self.options.api_port, - controller=self) + self.napps_manager, self.options.napps) self._register_endpoints() #: Adding the napps 'enabled' directory into the PATH From c4ae907709bb06c78d18384d846fe569d89624f8 Mon Sep 17 00:00:00 2001 From: Rogerio Motitsuki Date: Wed, 22 May 2019 16:03:51 -0300 Subject: [PATCH 5/6] Fix to hdiogenes comments to PR #903 --- kytos/core/api_server.py | 36 ++++++++++++++++++------------------ kytos/core/napps/manager.py | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/kytos/core/api_server.py b/kytos/core/api_server.py index baa8fc8d0..34ef4f04b 100644 --- a/kytos/core/api_server.py +++ b/kytos/core/api_server.py @@ -38,7 +38,7 @@ def __init__(self, app_name, listen='0.0.0.0', port=8181, controller(kytos.core.controller): A controller instance. """ dirname = os.path.dirname(os.path.abspath(__file__)) - self.nappsManager = napps_manager + self.napps_manager = napps_manager self.napps_dir = napps_dir self.flask_dir = os.path.join(dirname, '../web-ui') @@ -373,16 +373,16 @@ def _enable_napp(self, username, napp_name): :return: JSON content and return code """ # Check if the NApp is installed - if not self.nappsManager.is_installed(username, napp_name): + if not self.napps_manager.is_installed(username, napp_name): return '{"response": "not installed"}', \ HTTPStatus.BAD_REQUEST.value # Check if the NApp is already been enabled - if not self.nappsManager.is_enabled(username, napp_name): - self.nappsManager.enable(username, napp_name) + if not self.napps_manager.is_enabled(username, napp_name): + self.napps_manager.enable(username, napp_name) # Check if NApp is enabled - if not self.nappsManager.is_enabled(username, napp_name): + if not self.napps_manager.is_enabled(username, napp_name): # If it is not enabled an admin user must check the log file return '{"response": "error"}', \ HTTPStatus.INTERNAL_SERVER_ERROR.value @@ -398,16 +398,16 @@ def _disable_napp(self, username, napp_name): :return: JSON content and return code """ # Check if the NApp is installed - if not self.nappsManager.is_installed(username, napp_name): + if not self.napps_manager.is_installed(username, napp_name): return '{"response": "not installed"}', \ HTTPStatus.BAD_REQUEST.value # Check if the NApp is enabled - if self.nappsManager.is_enabled(username, napp_name): - self.nappsManager.disable(username, napp_name) + if self.napps_manager.is_enabled(username, napp_name): + self.napps_manager.disable(username, napp_name) # Check if NApp is still enabled - if self.nappsManager.is_enabled(username, napp_name): + if self.napps_manager.is_enabled(username, napp_name): # If it is still enabled an admin user must check the log file return '{"response": "error"}', \ HTTPStatus.INTERNAL_SERVER_ERROR.value @@ -417,13 +417,13 @@ def _disable_napp(self, username, napp_name): def _install_napp(self, username, napp_name): # Check if the NApp is installed - if self.nappsManager.is_installed(username, napp_name): + if self.napps_manager.is_installed(username, napp_name): return '{"response": "installed"}', HTTPStatus.OK.value napp = "{}/{}".format(username, napp_name) # Try to install the napp - if not self.nappsManager.install(napp, enable=False): + if not self.napps_manager.install(napp, enable=False): # If it is not installed an admin user must check the log file return '{"response": "error"}', \ HTTPStatus.INTERNAL_SERVER_ERROR.value @@ -432,9 +432,9 @@ def _install_napp(self, username, napp_name): def _uninstall_napp(self, username, napp_name): # Check if the NApp is installed - if self.nappsManager.is_installed(username, napp_name): + if self.napps_manager.is_installed(username, napp_name): # Try to unload/uninstall the napp - if not self.nappsManager.uninstall(username, napp_name): + if not self.napps_manager.uninstall(username, napp_name): # If it is not uninstalled admin user must check the log file return '{"response": "error"}', \ HTTPStatus.INTERNAL_SERVER_ERROR.value @@ -444,7 +444,7 @@ def _uninstall_napp(self, username, napp_name): def _list_enabled_napps(self): """Sorted list of (username, napp_name) of enabled napps.""" serialized_dict = json.dumps( - self.nappsManager.get_enabled_napps(), + self.napps_manager.get_enabled_napps(), default=lambda a: [a.username, a.name]) return '{"napps": %s}' % serialized_dict, HTTPStatus.OK.value @@ -452,7 +452,7 @@ def _list_enabled_napps(self): def _list_installed_napps(self): """Sorted list of (username, napp_name) of installed napps.""" serialized_dict = json.dumps( - self.nappsManager.get_installed_napps(), + self.napps_manager.get_installed_napps(), default=lambda a: [a.username, a.name]) return '{"napps": %s}' % serialized_dict, HTTPStatus.OK.value @@ -466,13 +466,13 @@ def _get_napp_metadata(self, username, napp_name, key): """ _VALID_KEYS = ['napp_dependencies', 'description', 'version'] - if not self.nappsManager.is_installed(username, napp_name): - return "Napp is not installed.", HTTPStatus.BAD_REQUEST.value + if not self.napps_manager.is_installed(username, napp_name): + return "NApp is not installed.", HTTPStatus.BAD_REQUEST.value if key not in _VALID_KEYS: return "Invalid key.", HTTPStatus.BAD_REQUEST.value - data = self.nappsManager.get_napp_metadata(username, napp_name, key) + data = self.napps_manager.get_napp_metadata(username, napp_name, key) serialized_dict = json.dumps({key: data}) return '%s' % serialized_dict, HTTPStatus.OK.value diff --git a/kytos/core/napps/manager.py b/kytos/core/napps/manager.py index e4274650b..a0ccee725 100644 --- a/kytos/core/napps/manager.py +++ b/kytos/core/napps/manager.py @@ -230,7 +230,7 @@ def get_napp_metadata(self, username, napp_name, key): meta = json.load(file_descriptor) return meta[key] except (FileNotFoundError, json.JSONDecodeError, KeyError): - LOG.warning("Load napp metada failed: %s/kytos.json", napp_id) + LOG.warning("NApp metadata load failed: %s/kytos.json", napp_id) return '' @staticmethod From 484036a38d3fb0cd49c91c27930aa0c6fcb619ad Mon Sep 17 00:00:00 2001 From: Rogerio Motitsuki Date: Wed, 22 May 2019 17:45:17 -0300 Subject: [PATCH 6/6] Fix linter issues --- kytos/core/api_server.py | 5 +++-- kytos/core/connection.py | 2 +- kytos/core/controller.py | 4 ++-- kytos/core/interface.py | 1 + tests/test_core/test_interface.py | 2 +- tests/test_core/test_switch.py | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/kytos/core/api_server.py b/kytos/core/api_server.py index 34ef4f04b..4dbf7b245 100644 --- a/kytos/core/api_server.py +++ b/kytos/core/api_server.py @@ -25,6 +25,7 @@ class APIServer: _NAPP_PREFIX = "/api/{napp.username}/{napp.name}/" _CORE_PREFIX = "/api/kytos/core/" + # pylint: disable=too-many-arguments def __init__(self, app_name, listen='0.0.0.0', port=8181, napps_manager=None, napps_dir=None): """Start a Flask+SocketIO server. @@ -464,12 +465,12 @@ def _get_napp_metadata(self, username, napp_name, key): napp_dependencies, description, version. """ - _VALID_KEYS = ['napp_dependencies', 'description', 'version'] + valid_keys = ['napp_dependencies', 'description', 'version'] if not self.napps_manager.is_installed(username, napp_name): return "NApp is not installed.", HTTPStatus.BAD_REQUEST.value - if key not in _VALID_KEYS: + if key not in valid_keys: return "Invalid key.", HTTPStatus.BAD_REQUEST.value data = self.napps_manager.get_napp_metadata(username, napp_name, key) diff --git a/kytos/core/connection.py b/kytos/core/connection.py index a5d1ea54a..5f6a27627 100644 --- a/kytos/core/connection.py +++ b/kytos/core/connection.py @@ -2,8 +2,8 @@ import logging from enum import Enum from errno import EBADF, ENOTCONN -from socket import error as SocketError from socket import SHUT_RDWR +from socket import error as SocketError __all__ = ('Connection', 'ConnectionProtocol', 'ConnectionState') diff --git a/kytos/core/controller.py b/kytos/core/controller.py index ebb5df79f..ddad94ca1 100644 --- a/kytos/core/controller.py +++ b/kytos/core/controller.py @@ -22,8 +22,8 @@ import sys import threading from concurrent.futures import ThreadPoolExecutor -from importlib import reload as reload_module from importlib import import_module +from importlib import reload as reload_module from importlib.util import module_from_spec, spec_from_file_location from pathlib import Path @@ -571,7 +571,7 @@ def set_switch_options(self, dpid): self.log.error("Invalid vlan_pool settings: %s", err) if vlan_pool.get(dpid): - self.log.info(f"Loading vlan_pool configuration for dpid {dpid}") + self.log.info("Loading vlan_pool configuration for dpid %s", dpid) for intf_num, port_list in vlan_pool[dpid].items(): if not switch.interfaces.get((intf_num)): vlan_ids = set() diff --git a/kytos/core/interface.py b/kytos/core/interface.py index 411260405..55055d9f7 100644 --- a/kytos/core/interface.py +++ b/kytos/core/interface.py @@ -107,6 +107,7 @@ def id(self): # pylint: disable=invalid-name Returns: string: Interface id. + """ return "{}:{}".format(self.switch.dpid, self.port_number) diff --git a/tests/test_core/test_interface.py b/tests/test_core/test_interface.py index 4047a3658..04eb61271 100644 --- a/tests/test_core/test_interface.py +++ b/tests/test_core/test_interface.py @@ -5,7 +5,7 @@ from pyof.v0x04.common.port import PortFeatures -from kytos.core.interface import Interface, TAG, TAGType +from kytos.core.interface import TAG, Interface, TAGType from kytos.core.switch import Switch logging.basicConfig(level=logging.CRITICAL) diff --git a/tests/test_core/test_switch.py b/tests/test_core/test_switch.py index 808902282..e44e565c0 100644 --- a/tests/test_core/test_switch.py +++ b/tests/test_core/test_switch.py @@ -28,7 +28,7 @@ def tearDown(self): def test_switch_vlan_pool_default(self): """Test default vlan_pool value.""" - self.assertEqual(self.options.vlan_pool, {}) + self.assertEqual(self.options.vlan_pool, '{}') def test_switch_vlan_pool_options(self): """Test switch with the example from kytos.conf."""