Skip to content
This repository has been archived by the owner on Apr 22, 2024. It is now read-only.

Commit

Permalink
Merge 617353c into e7d2ff2
Browse files Browse the repository at this point in the history
  • Loading branch information
rmotitsuki committed May 13, 2019
2 parents e7d2ff2 + 617353c commit f04d11c
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 14 deletions.
158 changes: 151 additions & 7 deletions kytos/core/api_server.py
Expand Up @@ -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

Expand All @@ -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):
napps_manager=None, napps_dir=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 = napps_manager
self.napps_dir = napps_dir

self.flask_dir = os.path.join(dirname, '../web-ui')
self.log = logging.getLogger('api_server')

Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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."""
Expand All @@ -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.
Expand Down Expand Up @@ -332,3 +340,139 @@ 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/<username>/<napp_name>/enable",
self._enable_napp)
self.register_core_endpoint("napps/<username>/<napp_name>/disable",
self._disable_napp)
self.register_core_endpoint("napps/<username>/<napp_name>/install",
self._install_napp)
self.register_core_endpoint("napps/<username>/<napp_name>/uninstall",
self._uninstall_napp)
self.register_core_endpoint("napps_enabled",
self._list_enabled_napps)
self.register_core_endpoint("napps_installed",
self._list_installed_napps)
self.register_core_endpoint(
"napps/<username>/<napp_name>/metadata/<key>",
self._get_napp_metadata)

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 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

return '{"response": "enabled"}', HTTPStatus.OK.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, enable=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 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

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

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
12 changes: 6 additions & 6 deletions kytos/core/controller.py
Expand Up @@ -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.<username>.<napp_name> import ?....
Expand Down
24 changes: 23 additions & 1 deletion kytos/core/napps/manager.py
Expand Up @@ -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``."""
Expand All @@ -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_.
Expand Down

0 comments on commit f04d11c

Please sign in to comment.