WebServices and New Wamp Agent routing

Wamp RPCs are forwarding using the correct server and topic
Introduced the new WebServices core feature with its REST APIs and models.
Designate and Nginx are required for this new feature.

Change-Id: Ia172654fbaf5502e3b9325a862a4e986fa27ee35
This commit is contained in:
Fabio Verboso 2018-12-06 12:03:34 +01:00
parent 683d52042c
commit 5a373e7941
25 changed files with 2080 additions and 80 deletions

View File

@ -2,7 +2,8 @@
transport_url=rabbit://<user>:<password>@<host>:5672/
debug=True
verbose=False
proxy=nginx
# dns_zone=openstack.iotronic
# Authentication strategy used by iotronic-api: one of
@ -40,6 +41,7 @@ password = <password>
[neutron]
auth_url = http://<keystone_host>:35357
url = http://<neutron_host>:9696
auth_strategy = password
project_domain_name = default
@ -50,7 +52,21 @@ username = neutron
password = <password>
retries = 3
project_domain_id= default
[designate]
auth_url = http://<keystone_host>:35357
url = http://<designate_host>:9001
auth_strategy = password
project_domain_name = default
user_domain_name = default
region_name = RegionOne
project_name = service
username = designate
password = <password>
retries = 3
project_domain_id= default
[cors]
# Indicate whether this resource may be shared with the domain

View File

@ -25,16 +25,13 @@ from wsme import types as wtypes
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import board
from iotronic.api.controllers.v1 import enabledwebservice
from iotronic.api.controllers.v1 import fleet
from iotronic.api.controllers.v1 import plugin
from iotronic.api.controllers.v1 import port
from iotronic.api.controllers.v1 import service
# from iotronic.api.controllers.v1 import driver
# from iotronic.api.controllers.v1 import portgroup
# from iotronic.api.controllers.v1 import ramdisk
# from iotronic.api.controllers.v1 import utils
from iotronic.api.controllers.v1 import board
from iotronic.api.controllers.v1 import webservice
from iotronic.api.controllers.v1 import versions
from iotronic.api import expose
@ -63,16 +60,22 @@ class V1(base.APIBase):
"""Links to the boards resource"""
plugins = [link.Link]
"""Links to the boards resource"""
"""Links to the plugins resource"""
services = [link.Link]
"""Links to the boards resource"""
"""Links to the services resource"""
enabledservices = [link.Link]
"""Links to the services resource"""
ports = [link.Link]
"""Links to the boards resource"""
"""Links to the ports resource"""
fleet = [link.Link]
"""Links to the boards resource"""
"""Links to the fleets resource"""
webservices = [link.Link]
"""Links to the webservices resource"""
@staticmethod
def convert():
@ -111,6 +114,15 @@ class V1(base.APIBase):
bookmark=True)
]
v1.enabledwebservices = [
link.Link.make_link('self', pecan.request.public_url,
'enabledwebservices', ''),
link.Link.make_link('bookmark',
pecan.request.public_url,
'enabledwebservices', '',
bookmark=True)
]
v1.ports = [link.Link.make_link('self', pecan.request.public_url,
'ports', ''),
link.Link.make_link('bookmark',
@ -125,6 +137,14 @@ class V1(base.APIBase):
bookmark=True)
]
v1.webservices = [link.Link.make_link('self', pecan.request.public_url,
'webservices', ''),
link.Link.make_link('bookmark',
pecan.request.public_url,
'webservices', '',
bookmark=True)
]
return v1
@ -134,8 +154,10 @@ class Controller(rest.RestController):
boards = board.BoardsController()
plugins = plugin.PluginsController()
services = service.ServicesController()
enabledwebservices = enabledwebservice.EnabledWebservicesController()
ports = port.PortsController()
fleets = fleet.FleetsController()
webservices = webservice.WebservicesController()
@expose.expose(V1)
def get(self):
@ -153,19 +175,20 @@ class Controller(rest.RestController):
"Mutually exclusive versions requested. Version %(ver)s "
"requested but not supported by this service. The supported "
"version range is: [%(min)s, %(max)s].") % {
'ver': version, 'min': versions.MIN_VERSION_STRING,
'max': versions.MAX_VERSION_STRING
}, headers=headers)
'ver': version,
'min': versions.MIN_VERSION_STRING,
'max': versions.MAX_VERSION_STRING
}, headers=headers)
# ensure the minor version is within the supported range
if version < MIN_VER or version > MAX_VER:
raise exc.HTTPNotAcceptable(_(
"Version %(ver)s was requested but the minor version is not "
"supported by this service. The supported version range is: "
"[%(min)s, %(max)s].") % {
'ver': version,
'min': versions.MIN_VERSION_STRING,
'max': versions.MAX_VERSION_STRING
}, headers=headers)
'ver': version,
'min': versions.MIN_VERSION_STRING,
'max': versions.MAX_VERSION_STRING
}, headers=headers)
@pecan.expose()
def _route(self, args):

View File

@ -14,9 +14,12 @@
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import collection
from iotronic.api.controllers.v1.enabledwebservice import EnabledWebservice
from iotronic.api.controllers.v1 import location as loc
from iotronic.api.controllers.v1 import types
from iotronic.api.controllers.v1 import utils as api_utils
from iotronic.api.controllers.v1.webservice import Webservice
from iotronic.api.controllers.v1.webservice import WebserviceCollection
from iotronic.api import expose
from iotronic.common import exception
from iotronic.common import policy
@ -27,11 +30,13 @@ import wsme
from wsme import types as wtypes
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
LOG = logging.getLogger(__name__)
_DEFAULT_RETURN_FIELDS = ('name', 'code', 'status', 'uuid', 'session', 'type',
'fleet')
_DEFAULT_WEBSERVICE_RETURN_FIELDS = ('name', 'uuid', 'port', 'board_uuid',
'extra')
class Board(base.APIBase):
@ -124,7 +129,6 @@ class BoardCollection(collection.Collection):
class Port(base.APIBase):
board_uuid = types.uuid
uuid = types.uuid
VIF_name = wtypes.text
@ -146,7 +150,6 @@ class Port(base.APIBase):
class PortCollection(collection.Collection):
"""API representation of a collection of ports."""
ports = [Port]
@ -373,7 +376,6 @@ class BoardPluginsController(rest.RestController):
class BoardServicesController(rest.RestController):
_custom_actions = {
'action': ['POST'],
'restore': ['GET']
@ -452,6 +454,167 @@ class BoardServicesController(rest.RestController):
return self._get_services_on_board_collection(rpc_board.uuid)
class BoardWebservicesController(rest.RestController):
_custom_actions = {
'enable': ['POST'],
'disable': ['DELETE']
}
invalid_sort_key_list = ['extra', ]
def __init__(self, board_ident):
self.board_ident = board_ident
def _get_webservices_collection(self, board, marker, limit,
sort_key, sort_dir,
fields=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
marker_obj = objects.Webservice.get_by_uuid(pecan.request.context,
marker)
if sort_key in self.invalid_sort_key_list:
raise exception.InvalidParameterValue(
("The sort_key value %(key)s is an invalid field for "
"sorting") % {'key': sort_key})
filters = {}
filters['board_uuid'] = board
webservices = objects.Webservice.list(pecan.request.context, limit,
marker_obj,
sort_key=sort_key,
sort_dir=sort_dir,
filters=filters)
parameters = {'sort_key': sort_key, 'sort_dir': sort_dir}
return WebserviceCollection.convert_with_links(webservices, limit,
fields=fields,
**parameters)
@expose.expose(WebserviceCollection, types.uuid, int, wtypes.text,
wtypes.text, types.listtype, types.boolean, types.boolean)
def get_all(self, marker=None,
limit=None, sort_key='id', sort_dir='asc',
fields=None):
"""Retrieve a list of webservices.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
This value cannot be larger than the value of max_limit
in the [api] section of the ironic configuration, or only
max_limit resources will be returned.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
:param with_public: Optional boolean to get also public pluings.
:param all_webservices: Optional boolean to get all the pluings.
Only for the admin
:param fields: Optional, a list with a specified set of fields
of the resource to be returned.
"""
cdict = pecan.request.context.to_policy_values()
policy.authorize('iot:webservice:get', cdict, cdict)
rpc_board = api_utils.get_rpc_board(self.board_ident)
filters = {}
filters['board_uuid'] = rpc_board.uuid
if fields is None:
fields = _DEFAULT_WEBSERVICE_RETURN_FIELDS
return self._get_webservices_collection(rpc_board.uuid, marker,
limit, sort_key, sort_dir,
fields=fields)
@expose.expose(Webservice, body=Webservice, status_code=201)
def put(self, Webservice):
"""Create a new Webservice.
:param Webservice: a Webservice within the request body.
"""
context = pecan.request.context
cdict = context.to_policy_values()
policy.authorize('iot:webservice:create', cdict, cdict)
if not Webservice.name:
raise exception.MissingParameterValue(
("Name is not specified."))
if Webservice.name:
if not api_utils.is_valid_name(Webservice.name):
msg = ("Cannot create webservice with invalid name %(name)s")
raise wsme.exc.ClientSideError(msg % {'name': Webservice.name},
status_code=400)
rpc_board = api_utils.get_rpc_board(self.board_ident)
new_Webservice = objects.Webservice(pecan.request.context,
**Webservice.as_dict())
new_Webservice.board_uuid = rpc_board.uuid
new_Webservice = pecan.request.rpcapi.create_webservice(
pecan.request.context,
new_Webservice)
return Webservice.convert_with_links(new_Webservice)
class EnabledWebserverData(base.APIBase):
dns = wtypes.text
zone = wtypes.text
email = wtypes.text
@expose.expose(EnabledWebservice, body=EnabledWebserverData,
status_code=201)
def enable(self, EnabledWebserverData):
"""Create a new Webservice.
:param Webservice: a Webservice within the request body.
"""
# context = pecan.request.context
# cdict = context.to_policy_values()
# policy.authorize('iot:webservice:create', cdict, cdict)
if not EnabledWebserverData.dns:
raise exception.MissingParameterValue(
("dns is not specified."))
if not EnabledWebserverData.zone:
raise exception.MissingParameterValue(
("zone is not specified."))
if not EnabledWebserverData.email:
raise exception.MissingParameterValue(
("email is not specified."))
rpc_board = api_utils.get_rpc_board(self.board_ident)
new_EnWebservice = pecan.request.rpcapi.enable_webservice(
pecan.request.context,
EnabledWebserverData.dns,
EnabledWebserverData.zone,
EnabledWebserverData.email,
rpc_board.uuid)
return EnabledWebservice.convert_with_links(new_EnWebservice)
@expose.expose(None, status_code=204)
def disable(self):
"""Disable webservices in a board.
:param board_ident: UUID or logical name of a board.
"""
# context = pecan.request.context
# cdict = context.to_policy_values()
# policy.authorize('iot:board:delete', cdict, cdict)
rpc_board = api_utils.get_rpc_board(self.board_ident)
pecan.request.rpcapi.disable_webservice(pecan.request.context,
rpc_board.uuid)
class BoardPortsController(rest.RestController):
def __init__(self, board_ident):
@ -486,7 +649,7 @@ class BoardPortsController(rest.RestController):
rpc_board.check_if_online()
result = pecan.request.rpcapi.\
result = pecan.request.rpcapi. \
create_port_on_board(pecan.request.context, rpc_board.uuid,
Network.network, Network.subnet,
Network.security_groups)
@ -539,6 +702,7 @@ class BoardsController(rest.RestController):
'plugins': BoardPluginsController,
'services': BoardServicesController,
'ports': BoardPortsController,
'webservices': BoardWebservicesController,
}
invalid_sort_key_list = ['extra', 'location']

View File

@ -0,0 +1,162 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import collection
from iotronic.api.controllers.v1 import types
from iotronic.api.controllers.v1 import utils as api_utils
from iotronic.api import expose
from iotronic.common import exception
from iotronic.common import policy
from iotronic import objects
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
_DEFAULT_RETURN_FIELDS = ('board_uuid', 'http_port', 'https_port',
'dns', 'zone', 'extra')
class EnabledWebservice(base.APIBase):
"""API representation of a enabled_webservice.
"""
http_port = wsme.types.IntegerType()
https_port = wsme.types.IntegerType()
board_uuid = types.uuid
dns = wsme.wsattr(wtypes.text)
zone = wsme.wsattr(wtypes.text)
extra = types.jsontype
links = wsme.wsattr([link.Link], readonly=True)
def __init__(self, **kwargs):
self.fields = []
fields = list(objects.EnabledWebservice.fields)
for k in fields:
# Skip fields we do not expose.
if not hasattr(self, k):
continue
self.fields.append(k)
setattr(self, k, kwargs.get(k, wtypes.Unset))
@staticmethod
def _convert(enabled_webservice, fields=None):
if fields is not None:
enabled_webservice.unset_fields_except(fields)
return enabled_webservice
@classmethod
def convert_with_links(cls, rpc_enabled_webservice, fields=None):
enabled_webservice = EnabledWebservice(
**rpc_enabled_webservice.as_dict())
if fields is not None:
api_utils.check_for_invalid_fields(fields,
enabled_webservice.as_dict())
return cls._convert(enabled_webservice,
fields=fields)
class EnabledWebserviceCollection(collection.Collection):
"""API representation of a collection of EnabledWebservices."""
EnabledWebservices = [EnabledWebservice]
"""A list containing EnabledWebservices objects"""
def __init__(self, **kwargs):
self._type = 'EnabledWebservices'
@staticmethod
def convert_with_links(EnabledWebservices, limit, url=None, fields=None,
**kwargs):
collection = EnabledWebserviceCollection()
collection.EnabledWebservices = [
EnabledWebservice.convert_with_links(n, fields=fields)
for n in EnabledWebservices]
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
class EnabledWebservicesController(rest.RestController):
"""REST controller for EnabledWebservices."""
invalid_sort_key_list = ['extra', ]
def _get_EnabledWebservices_collection(self, marker, limit,
sort_key, sort_dir, project_id,
fields=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
marker_obj = objects.EnabledWebservice.get_by_id(
pecan.request.context,
marker)
if sort_key in self.invalid_sort_key_list:
raise exception.InvalidParameterValue(
("The sort_key value %(key)s is an invalid field for "
"sorting") % {'key': sort_key})
filters = {}
filters['project_id'] = project_id
EnabledWebservices = objects.EnabledWebservice.list(
pecan.request.context, limit,
marker_obj,
sort_key=sort_key,
sort_dir=sort_dir,
filters=filters)
parameters = {'sort_key': sort_key, 'sort_dir': sort_dir}
return EnabledWebserviceCollection.convert_with_links(
EnabledWebservices, limit,
fields=fields,
**parameters)
@expose.expose(EnabledWebserviceCollection, types.uuid, int, wtypes.text,
wtypes.text, types.listtype)
def get_all(self, marker=None,
limit=None, sort_key='id', sort_dir='asc',
fields=None):
"""Retrieve a list of EnabledWebservices.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
This value cannot be larger than the value of max_limit
in the [api] section of the ironic configuration, or only
max_limit resources will be returned.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
:param fields: Optional, a list with a specified set of fields
of the resource to be returned.
"""
cdict = pecan.request.context.to_policy_values()
policy.authorize('iot:enabledwebservice:get', cdict, cdict)
project_id = pecan.request.context.project_id
if fields is None:
fields = _DEFAULT_RETURN_FIELDS
return self._get_EnabledWebservices_collection(marker,
limit,
sort_key, sort_dir,
project_id=project_id,
fields=fields)

View File

@ -147,6 +147,33 @@ def get_rpc_service(service_ident):
raise exception.ServiceNotFound(service=service_ident)
def get_rpc_webservice(webservice_ident):
"""Get the RPC webservice from the webservice uuid or logical name.
:param webservice_ident: the UUID or logical name of a webservice.
:returns: The RPC Webservice.
:raises: InvalidUuidOrName if the name or uuid provided is not valid.
:raises: WebserviceNotFound if the webservice is not found.
"""
# Check to see if the webservice_ident is a valid UUID. If it is, treat it
# as a UUID.
if uuidutils.is_uuid_like(webservice_ident):
return objects.Webservice.get_by_uuid(pecan.request.context,
webservice_ident)
# # We can refer to webservices by their name, if the client supports it
# # if allow_webservice_logical_names():
# # if utils.is_hostname_safe(webservice_ident):
# else:
# return objects.Webservice.get_by_name(pecan.request.context,
# webservice_ident)
raise exception.InvalidUuidOrName(name=webservice_ident)
raise exception.WebserviceNotFound(webservice=webservice_ident)
def get_rpc_port(port_ident):
"""Get the RPC port from the port uuid.

View File

@ -0,0 +1,277 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from iotronic.api.controllers import base
from iotronic.api.controllers import link
from iotronic.api.controllers.v1 import collection
from iotronic.api.controllers.v1 import types
from iotronic.api.controllers.v1 import utils as api_utils
from iotronic.api import expose
from iotronic.common import exception
from iotronic.common import policy
from iotronic import objects
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
_DEFAULT_RETURN_FIELDS = ('name', 'uuid', 'port', 'board_uuid', 'extra')
class Webservice(base.APIBase):
"""API representation of a webservice.
"""
uuid = types.uuid
name = wsme.wsattr(wtypes.text)
port = wsme.types.IntegerType()
board_uuid = types.uuid
secure = types.boolean
extra = types.jsontype
links = wsme.wsattr([link.Link], readonly=True)
def __init__(self, **kwargs):
self.fields = []
fields = list(objects.Webservice.fields)
for k in fields:
# Skip fields we do not expose.
if not hasattr(self, k):
continue
self.fields.append(k)
setattr(self, k, kwargs.get(k, wtypes.Unset))
@staticmethod
def _convert_with_links(webservice, url, fields=None):
webservice_uuid = webservice.uuid
if fields is not None:
webservice.unset_fields_except(fields)
webservice.links = [link.Link.make_link('self', url, 'webservices',
webservice_uuid),
link.Link.make_link('bookmark', url, 'webservices',
webservice_uuid, bookmark=True)
]
return webservice
@classmethod
def convert_with_links(cls, rpc_webservice, fields=None):
webservice = Webservice(**rpc_webservice.as_dict())
if fields is not None:
api_utils.check_for_invalid_fields(fields, webservice.as_dict())
return cls._convert_with_links(webservice, pecan.request.public_url,
fields=fields)
class WebserviceCollection(collection.Collection):
"""API representation of a collection of webservices."""
webservices = [Webservice]
"""A list containing webservices objects"""
def __init__(self, **kwargs):
self._type = 'webservices'
@staticmethod
def convert_with_links(webservices, limit, url=None, fields=None,
**kwargs):
collection = WebserviceCollection()
collection.webservices = [Webservice.convert_with_links(n,
fields=fields)
for n in webservices]
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
class WebservicesController(rest.RestController):
"""REST controller for Webservices."""
# _subcontroller_map = {
# 'boards': WebserviceBoardsController,
# }
invalid_sort_key_list = ['extra', ]
_custom_actions = {
'detail': ['GET'],
}
# @pecan.expose()
# def _lookup(self, ident, *remainder):
# try:
# ident = types.uuid_or_name.validate(ident)
# except exception.InvalidUuidOrName as e:
# pecan.abort('400', e.args[0])
# if not remainder:
# return
#
# subcontroller = self._subcontroller_map.get(remainder[0])
# if subcontroller:
# return subcontroller(webservice_ident=ident), remainder[1:]
def _get_webservices_collection(self, marker, limit,
sort_key, sort_dir,
project_id,
fields=None):
limit = api_utils.validate_limit(limit)
sort_dir = api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
marker_obj = objects.Webservice.get_by_uuid(pecan.request.context,
marker)
if sort_key in self.invalid_sort_key_list:
raise exception.InvalidParameterValue(
("The sort_key value %(key)s is an invalid field for "
"sorting") % {'key': sort_key})
filters = {}
filters['project_id'] = project_id
webservices = objects.Webservice.list(pecan.request.context, limit,
marker_obj,
sort_key=sort_key,
sort_dir=sort_dir,
filters=filters)
parameters = {'sort_key': sort_key, 'sort_dir': sort_dir}
return WebserviceCollection.convert_with_links(webservices, limit,
fields=fields,
**parameters)
@expose.expose(Webservice, types.uuid_or_name, types.listtype)
def get_one(self, webservice_ident, fields=None):
"""Retrieve information about the given webservice.
:param webservice_ident: UUID or logical name of a webservice.
:param fields: Optional, a list with a specified set of fields
of the resource to be returned.
"""
rpc_webservice = api_utils.get_rpc_webservice(webservice_ident)
cdict = pecan.request.context.to_policy_values()
policy.authorize('iot:webservice:get_one', cdict, cdict)
return Webservice.convert_with_links(rpc_webservice, fields=fields)
@expose.expose(WebserviceCollection, types.uuid, int, wtypes.text,
wtypes.text, types.listtype, types.boolean, types.boolean)
def get_all(self, marker=None,
limit=None, sort_key='id', sort_dir='asc',
fields=None):
"""Retrieve a list of webservices.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
This value cannot be larger than the value of max_limit
in the [api] section of the ironic configuration, or only
max_limit resources will be returned.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
:param with_public: Optional boolean to get also public pluings.
:param all_webservices: Optional boolean to get all the pluings.
Only for the admin
:param fields: Optional, a list with a specified set of fields
of the resource to be returned.
"""
cdict = pecan.request.context.to_policy_values()
policy.authorize('iot:webservice:get', cdict, cdict)
project_id = pecan.request.context.project_id
if fields is None:
fields = _DEFAULT_RETURN_FIELDS
return self._get_webservices_collection(marker,
limit, sort_key, sort_dir,
project_id=project_id,
fields=fields)
@expose.expose(None, types.uuid_or_name, status_code=204)
def delete(self, webservice_ident):
"""Delete a webservice.
:param webservice_ident: UUID or logical name of a webservice.
"""
context = pecan.request.context
cdict = context.to_policy_values()
policy.authorize('iot:webservice:delete', cdict, cdict)
rpc_webservice = api_utils.get_rpc_webservice(webservice_ident)
pecan.request.rpcapi.destroy_webservice(pecan.request.context,
rpc_webservice.uuid)
# @expose.expose(Webservice, types.uuid_or_name, body=Webservice,
# status_code=200)
# def patch(self, webservice_ident, val_Webservice):
# """Update a webservice.
#
# :param webservice_ident: UUID or logical name of a webservice.
# :param Webservice: values to be changed
# :return updated_webservice: updated_webservice
# """
#
# rpc_webservice = api_utils.get_rpc_webservice(webservice_ident)
# cdict = pecan.request.context.to_policy_values()
# cdict['project'] = rpc_webservice.project
# policy.authorize('iot:webservice:update', cdict, cdict)
#
# val_Webservice = val_Webservice.as_dict()
# for key in val_Webservice:
# try:
# rpc_webservice[key] = val_Webservice[key]
# except Exception:
# pass
#
# updated_webservice = pecan.request.rpcapi.update_webservice(
# pecan.request.context, rpc_webservice)
# return Webservice.convert_with_links(updated_webservice)
@expose.expose(WebserviceCollection, types.uuid, int, wtypes.text,
wtypes.text, types.listtype, types.boolean, types.boolean)
def detail(self, marker=None,
limit=None, sort_key='id', sort_dir='asc',
fields=None, with_public=False, all_webservs=False):
"""Retrieve a list of webservices.
:param marker: pagination marker for large data sets.
:param limit: maximum number of resources to return in a single result.
This value cannot be larger than the value of max_limit
in the [api] section of the ironic configuration, or only
max_limit resources will be returned.
:param sort_key: column to sort results by. Default: id.
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
:param with_public: Optional boolean to get also public webservice.
:param all_webservs: Optional boolean to get all the webservices.
Only for the admin
:param fields: Optional, a list with a specified set of fields
of the resource to be returned.
"""
cdict = pecan.request.context.to_policy_values()
policy.authorize('iot:webservice:get', cdict, cdict)
# /detail should only work against collections
parent = pecan.request.path.split('/')[:-1][-1]
if parent != "webservices":
raise exception.HTTPNotFound()
return self._get_webservices_collection(marker,
limit, sort_key, sort_dir,
with_public=with_public,
all_webservices=all_webservs,
fields=fields)

View File

@ -0,0 +1,92 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from designateclient.v2 import client
from keystoneauth1.identity import generic
from keystoneauth1 import session as keystone_session
from oslo_config import cfg
CONF = cfg.CONF
designate_opts = [
cfg.StrOpt('url',
default='http://localhost:9696/',
help=('URL designate')),
cfg.StrOpt('retries',
default=3,
help=('retries designate')),
cfg.StrOpt('auth_strategy',
default='noauth',
help=('auth_strategy designate')),
cfg.StrOpt('username',
default='designate',
help=('designate username')),
cfg.StrOpt('password',
default='',
help=('password')),
cfg.StrOpt('project_name',
default='service',
help=('service')),
cfg.StrOpt('project_domain_name',
default='default',
help=('domain id')),
cfg.StrOpt('auth_url',
default='http://localhost:35357',
help=('auth')),
cfg.StrOpt('project_domain_id',
default='default',
help=('project domain id')),
cfg.StrOpt('user_domain_id',
default='default',
help=('user domain id')),
]
CONF.register_opts(designate_opts, 'designate')
def get_client():
auth = generic.Password(
auth_url=CONF.designate.auth_url,
username=CONF.designate.username,
password=CONF.designate.password,
project_name=CONF.designate.project_name,
project_domain_id=CONF.designate.project_domain_id,
user_domain_id=CONF.designate.user_domain_id,
)
session = keystone_session.Session(auth=auth)
cl = client.Client(session=session)
return cl
def create_record(name, ip, zone_name):
client = get_client()
zone = client.zones.get(zone_name + ".")
record = None
try:
record = client.recordsets.get(zone["id"], name + "." + zone["name"])
except Exception:
pass
if not record:
client.recordsets.create(zone["id"], name, 'A', [ip])
def delete_record(name, zone_name):
client = get_client()
zone = client.zones.get(zone_name + ".")
try:
record = client.recordsets.get(zone["id"], name + "." + zone["name"])
except Exception:
pass
if record:
client.recordsets.delete(zone["id"], name + "." + zone["name"])

View File

@ -142,6 +142,11 @@ class BoardAlreadyExists(Conflict):
message = _("A board with UUID %(uuid)s already exists.")
class BoardInvalidStatus(Conflict):
message = _(
"A board with UUID %(uuid)s has a not valid state: %(status)s.")
class MACAlreadyExists(Conflict):
message = _("A port with MAC address %(mac)s already exists.")
@ -641,3 +646,19 @@ class FleetAlreadyExists(Conflict):
class FleetAlreadyExposed(Conflict):
message = _("A Fleet with UUID %(uuid)s already exposed.")
class WebserviceNotFound(NotFound):
message = _("Webservice %(Webservice)s could not be found.")
class WebserviceAlreadyExists(Conflict):
message = _("A Service with UUID %(uuid)s already exists.")
class EnabledWebserviceNotFound(NotFound):
message = _("No Webservice enabled for %(enabled_webservice)s.")
class EnabledWebserviceAlreadyExists(Conflict):
message = _("Already enabled for %(enabled_webservice)s.")

View File

@ -38,7 +38,7 @@ neutron_opts = [
default='neutron',
help=('neutron username')),
cfg.StrOpt('password',
default='0penstack',
default='',
help=('password')),
cfg.StrOpt('project_name',
default='service',

View File

@ -181,6 +181,31 @@ fleet_policies = [
]
webservice_policies = [
policy.RuleDefault('iot:webservice:get',
'rule:is_admin or rule:is_iot_member',
description='Retrieve Webservice records'),
policy.RuleDefault('iot:webservice:create',
'rule:is_iot_member',
description='Create Webservice records'),
policy.RuleDefault('iot:webservice:get_one', 'rule:admin_or_owner',
description='Retrieve a Webservice record'),
policy.RuleDefault('iot:webservice:delete', 'rule:admin_or_owner',
description='Delete Webservice records'),
policy.RuleDefault('iot:webservice:update', 'rule:admin_or_owner',
description='Update Webservice records'),
]
enabledwebservice_policies = [
policy.RuleDefault('iot:enabledwebservice:get',
'rule:is_admin or rule:is_iot_member',
description='Retrieve EnabledWebservice records'),
policy.RuleDefault('iot:enabledwebservice:get_one',
'rule:admin_or_owner',
description='Retrieve a EnabledWebservice record'),
]
def list_policies():
policies = (default_policies
@ -191,6 +216,8 @@ def list_policies():
+ exposed_service_policies
+ port_on_board_policies
+ fleet_policies
+ webservice_policies
+ enabledwebservice_policies
)
return policies

View File

@ -21,7 +21,7 @@ except Exception:
# allow iotronic api to run also with python2.7
import pickle as cpickle
from iotronic.common import exception
from iotronic.common import exception, designate
from iotronic.common import neutron
from iotronic.common import states
from iotronic.conductor.provisioner import Provisioner
@ -49,7 +49,7 @@ def get_best_agent(ctx):
def random_public_port():
return random.randint(6000, 7000)
return random.randint(50000, 60000)
def manage_result(res, wamp_rpc_call, board_uuid):
@ -68,13 +68,48 @@ def manage_result(res, wamp_rpc_call, board_uuid):
return res.message
def create_record_dns_webservice(ctx, board, webs_name, board_dns, zone):
agent = objects.WampAgent.get_by_hostname(ctx, board.agent)
wsurl = agent.wsurl
ip = wsurl.split("//")[1].split(":")[0]
LOG.debug('Create dns record %s for board %s',
webs_name + "." + board_dns + "." + zone,
board.uuid)
LOG.debug('using %s %s %s', webs_name + "." + board_dns,
ip, zone)
designate.create_record(webs_name + "." + board_dns, ip,
zone)
def create_record_dns(ctx, board, board_dns, zone):
agent = objects.WampAgent.get_by_hostname(ctx, board.agent)
wsurl = agent.wsurl
ip = wsurl.split("//")[1].split(":")[0]
LOG.debug('Create dns record %s for board %s',
board_dns + "." + zone,
board.uuid)
LOG.debug('using %s %s %s', board_dns,
ip, zone)
designate.create_record(board_dns, ip,
zone)
LOG.debug('Configure Web Proxy on WampAgent %s (%s) for board %s',
board.agent, ip, board.uuid)
class ConductorEndpoint(object):
def __init__(self, ragent):
transport = oslo_messaging.get_transport(cfg.CONF)
self.target = oslo_messaging.Target()
self.wamp_agent_client = oslo_messaging.RPCClient(transport,
self.target)
self.wamp_agent_client.prepare(timeout=10)
self.wamp_agent_client = self.wamp_agent_client.prepare(timeout=120,
topic='s4t')
self.ragent = ragent
def echo(self, ctx, data):
@ -180,19 +215,21 @@ class ConductorEndpoint(object):
wamp_rpc_call, board_uuid)
board = objects.Board.get_by_uuid(ctx, board_uuid)
# session should be taken from board, not from the object
s4t_topic = 's4t_invoke_wamp'
full_topic = board.agent + '.' + s4t_topic
self.target.topic = full_topic
full_wamp_call = 'iotronic.' + board.uuid + "." + wamp_rpc_call
session = objects.SessionWP.get_session_by_board_uuid(ctx, board_uuid)
full_wamp_call = 'iotronic.' + \
session.session_id + "." + \
board.uuid + "." + wamp_rpc_call
# check the session; it rise an excpetion if session miss
if not board.is_online():
raise exception.BoardNotConnected(board=board.uuid)
res = self.wamp_agent_client.call(ctx, full_topic,
wamp_rpc_call=full_wamp_call,
data=wamp_rpc_args)
cctx = self.wamp_agent_client.prepare(server=board.agent)
res = cctx.call(ctx, 's4t_invoke_wamp',
wamp_rpc_call=full_wamp_call,
data=wamp_rpc_args)
res = wm.deserialize(res)
return res
@ -415,10 +452,6 @@ class ConductorEndpoint(object):
Port.insert(0, port_socat)
r_tcp_port = str(port_socat)
s4t_topic = 'create_tap_interface'
full_topic = str(board.agent) + '.' + s4t_topic
self.target.topic = full_topic
try:
LOG.info('Creation of the VIF on the board')
self.execute_on_board(ctx, board_uuid, "Create_VIF",
@ -426,9 +459,10 @@ class ConductorEndpoint(object):
try:
LOG.debug('starting the wamp client')
self.wamp_agent_client.call(ctx, full_topic,
port_uuid=p,
tcp_port=r_tcp_port)
cctx = self.wamp_agent_client.prepare(server=board.agent)
cctx.call(ctx, 'create_tap_interface',
port_uuid=p,
tcp_port=r_tcp_port)
try:
LOG.info('Updating the DB')
@ -515,3 +549,239 @@ class ConductorEndpoint(object):
LOG.debug('Updating fleet %s', fleet.name)
fleet.save()
return serializer.serialize_entity(ctx, fleet)
def create_webservice(self, ctx, webservice_obj):
newwbs = serializer.deserialize_entity(ctx, webservice_obj)
LOG.debug('Creating webservice %s',
newwbs.name)
en_webservice = objects.enabledwebservice. \
EnabledWebservice.get_by_board_uuid(ctx,
newwbs.board_uuid)
full_zone_domain = en_webservice.dns + "." + en_webservice.zone
dns_domain = newwbs.name + "." + full_zone_domain
board = objects.Board.get_by_uuid(ctx, newwbs.board_uuid)
create_record_dns_webservice(ctx, board, newwbs.name,
en_webservice.dns,
en_webservice.zone)
LOG.debug('Creating webservice with full domain %s',
dns_domain)
list_webs = objects.Webservice.list(ctx,
filters={
'board_uuid': newwbs.board_uuid
})
list_dns = full_zone_domain + ","
for webs in list_webs:
dname = webs.name + "." + full_zone_domain + ","
list_dns = list_dns + dname
list_dns = list_dns + dns_domain
try:
self.execute_on_board(ctx,
newwbs.board_uuid,
'ExposeWebservice',
(full_zone_domain,
dns_domain,
newwbs.port,
list_dns,))
except exception:
return exception
newwbs.create()
return serializer.serialize_entity(ctx, newwbs)
def destroy_webservice(self, ctx, webservice_id):
LOG.info('Destroying webservice with id %s',
webservice_id)
wbsrv = objects.Webservice.get_by_uuid(ctx, webservice_id)
en_webservice = objects.enabledwebservice. \
EnabledWebservice.get_by_board_uuid(ctx,
wbsrv.board_uuid)
full_zone_domain = en_webservice.dns + "." + en_webservice.zone
dns_domain = wbsrv.name + "." + full_zone_domain
list_webs = objects.Webservice.list(ctx,
filters={
'board_uuid': wbsrv.board_uuid
})
list_dns = full_zone_domain + ","
for webs in list_webs:
if webs.name != wbsrv.name:
dname = webs.name + "." + full_zone_domain + ","
list_dns = list_dns + dname
list_dns = list_dns[:-1]
try:
# result =
self.execute_on_board(ctx,
wbsrv.board_uuid,
'UnexposeWebservice',
(dns_domain, list_dns,))
except exception:
return exception
wbsrv.destroy()
designate.delete_record(wbsrv.name + "." + en_webservice.dns,
en_webservice.zone)
return
def enable_webservice(self, ctx, dns, zone, email, board_uuid):
board = objects.Board.get_by_uuid(ctx, board_uuid)
if board.agent == None:
raise exception.BoardInvalidStatus(uuid=board.uuid,
status=board.status)
try:
create_record_dns(ctx, board, dns, zone)
except exception:
return exception
try:
en_webservice = objects.enabledwebservice. \
EnabledWebservice.get_by_board_uuid(ctx,
board.uuid)
LOG.debug('Webservice data already exists for board %s',
board.uuid)
https_port = en_webservice.https_port
http_port = en_webservice.http_port
except Exception:
# TO BE CHANGED
https_port = random_public_port()
http_port = random_public_port()
en_webservice = {
'board_uuid': board.uuid,
'http_port': http_port,
'https_port': https_port,
'dns': dns,
'zone': zone
}
en_webservice = objects.enabledwebservice.EnabledWebservice(
ctx, **en_webservice)
LOG.debug('Save webservice data %s for board %s', dns + "." + zone,
board.uuid)
en_webservice.create()
LOG.debug('Open ports on WampAgent %s for http and %s for https '
'on board %s', http_port, https_port, board.uuid)
service = objects.Service.get_by_name(ctx, 'webservice')
res = self.execute_on_board(ctx, board.uuid, "ServiceEnable",
(service, http_port))
result = manage_result(res, "ServiceEnable", board.uuid)
exp_data = {
'board_uuid': board_uuid,
'service_uuid': service.uuid,
'public_port': http_port,
}
exposed = objects.ExposedService(ctx, **exp_data)
exposed.create()
LOG.debug(result)
service = objects.Service.get_by_name(ctx, 'webservice_ssl')
res = self.execute_on_board(ctx, board.uuid, "ServiceEnable",
(service, https_port))
result = manage_result(res, "ServiceEnable", board.uuid)
exp_data = {
'board_uuid': board_uuid,
'service_uuid': service.uuid,
'public_port': https_port,
}
exposed = objects.ExposedService(ctx, **exp_data)
exposed.create()
LOG.debug(result)
cctx = self.wamp_agent_client.prepare(server=board.agent)
cctx.call(ctx, 'enable_webservice', board=dns,
https_port=https_port, http_port=http_port)
cctx.call(ctx, 'reload_proxy')
LOG.debug('Configure Web Proxy on Board %s with dns %s (email: %s) ',
board.uuid, dns, email)
try:
# result =
self.execute_on_board(ctx,
board.uuid,
'EnableWebService',
(dns + "." + zone, email,))
except exception:
return exception
return serializer.serialize_entity(ctx, en_webservice)
def disable_webservice(self, ctx, board_uuid):
LOG.info('Disabling webservice on board id %s',
board_uuid)
board = objects.Board.get_by_uuid(ctx, board_uuid)
if board.agent == None:
raise exception.BoardInvalidStatus(uuid=board.uuid,
status=board.status)
en_webservice = objects.enabledwebservice. \
EnabledWebservice.get_by_board_uuid(ctx,
board.uuid)
https_port = en_webservice.https_port
http_port = en_webservice.http_port
LOG.debug('Disable Webservices ports %s for http and %s for https '
'on board %s', http_port, https_port, board.uuid)
service = objects.Service.get_by_name(ctx, 'webservice')
exposed = objects.ExposedService.get(ctx,
board_uuid,
service.uuid)
res = self.execute_on_board(ctx, board.uuid, "ServiceDisable",
(service,))
LOG.debug(res.message)
exposed.destroy()
service = objects.Service.get_by_name(ctx, 'webservice_ssl')
exposed = objects.ExposedService.get(ctx,
board_uuid,
service.uuid)
res = self.execute_on_board(ctx, board.uuid, "ServiceDisable",
(service,))
LOG.debug(res.message)
exposed.destroy()
webservice = objects.EnabledWebservice.get_by_board_uuid(
ctx, board_uuid)
LOG.debug('Remove dns record %s for board %s',
webservice.dns, board.uuid)
designate.delete_record(webservice.dns, en_webservice.zone)
cctx = self.wamp_agent_client.prepare(server=board.agent)
cctx.call(ctx, 'disable_webservice', board=webservice.dns)
cctx.call(ctx, 'reload_proxy')
webservice.destroy()
return

View File

@ -351,3 +351,47 @@ class ConductorAPI(object):
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
return cctxt.call(context, 'update_fleet', fleet_obj=fleet_obj)
def create_webservice(self, context, webservice_obj, topic=None):
"""Add a webservice on the cloud
:param context: request context.
:param webservice_obj: a changed (but not saved) webservice object.
:param topic: RPC topic. Defaults to self.topic.
:returns: created webservice object
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
return cctxt.call(context, 'create_webservice',
webservice_obj=webservice_obj)
def destroy_webservice(self, context, webservice_id, topic=None):
"""Delete a webservice.
:param context: request context.
:param webservice_id: webservice id or uuid.
:raises: WebserviceLocked if webservice is locked by another conductor.
:raises: WebserviceAssociated if the webservice contains an instance
associated with it.
:raises: InvalidState if the webservice is in the wrong provision
state to perform deletion.
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
return cctxt.call(context, 'destroy_webservice',
webservice_id=webservice_id)
def enable_webservice(self, context, dns, zone, email, board, topic=None):
"""Eneble a webservice on the board
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
return cctxt.call(context, 'enable_webservice',
dns=dns, zone=zone, email=email, board_uuid=board)
def disable_webservice(self, context, board_uuid, topic=None):
"""Disable webservice manager.
"""
cctxt = self.client.prepare(topic=topic or self.topic, version='1.0')
return cctxt.call(context, 'disable_webservice',
board_uuid=board_uuid)

View File

@ -626,3 +626,86 @@ class Connection(object):
:raises: FleetAssociated
:raises: FleetNotFound
"""
@abc.abstractmethod
def get_webservice_by_id(self, webservice_id):
"""Return a webservice.
:param webservice_id: The id of a webservice.
:returns: A webservice.
"""
@abc.abstractmethod
def get_webservice_by_uuid(self, webservice_uuid):
"""Return a webservice.
:param webservice_uuid: The uuid of a webservice.
:returns: A webservice.
"""
@abc.abstractmethod
def get_webservice_by_name(self, webservice_name):
"""Return a webservice.
:param webservice_name: The logical name of a webservice.
:returns: A webservice.
"""
@abc.abstractmethod
def create_webservice(self, values):
"""Create a new webservice.
:param values: A dict containing several items used to identify
and track the webservice
:returns: A webservice.
"""
@abc.abstractmethod
def destroy_webservice(self, webservice_id):
"""Destroy a webservice and all associated interfaces.
:param webservice_id: The id or uuid of a webservice.
"""
@abc.abstractmethod
def update_webservice(self, webservice_id, values):
"""Update properties of a webservice.
:param webservice_id: The id or uuid of a webservice.
:param values: Dict of values to update.
:returns: A webservice.
:raises: WebserviceAssociated
:raises: WebserviceNotFound
"""
@abc.abstractmethod
def get_enabled_webservice_by_id(self, enabled_webservice_id):
"""Return a enabled_webservice.
:param enabled_webservice_id: The id of a enabled_webservice.
:returns: A enabled_webservice.
"""
@abc.abstractmethod
def get_enabled_webservice_by_board_uuid(self, board_uuid):
"""Return a enabled_webservice.
:param board_uuid: The uuid of a board enabled_webservice.
:returns: A enabled_webservice.
"""
@abc.abstractmethod
def create_enabled_webservice(self, values):
"""Create a new enabled_webservice.
:param values: A dict containing several items used to identify
and track the enabled_webservice
:returns: A enabled_webservice.
"""
@abc.abstractmethod
def destroy_enabled_webservice(self, enabled_webservice_id):
"""Destroy a enabled_webservice and all associated interfaces.
:param enabled_webservice_id: The id or uuid of a enabled_webservice.
"""

View File

@ -0,0 +1,61 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# revision identifiers, used by Alembic.
revision = 'f28f3f4494b3'
down_revision = '9c5c34dfd9f1'
from alembic import op
import iotronic.db.sqlalchemy.models
import sqlalchemy as sa
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
'enabled_webservices',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('board_uuid', sa.String(length=36), nullable=True),
sa.Column('http_port', sa.Integer(), nullable=True),
sa.Column('https_port', sa.Integer(), nullable=True),
sa.Column('dns', sa.String(length=100), nullable=True),
sa.Column('zone', sa.String(length=100), nullable=True),
sa.Column('extra',
iotronic.db.sqlalchemy.models.JSONEncodedDict(),
nullable=True),
sa.ForeignKeyConstraint(['board_uuid'], ['boards.uuid'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table(
'webservices',
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=True),
sa.Column('port', sa.Integer(), nullable=True),
sa.Column('name', sa.String(length=45), nullable=True),
sa.Column('board_uuid', sa.String(length=36), nullable=True),
sa.Column('secure', sa.Boolean(), nullable=True),
sa.Column('extra',
iotronic.db.sqlalchemy.models.JSONEncodedDict(),
nullable=True),
sa.ForeignKeyConstraint(['board_uuid'], ['boards.uuid'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('uuid',
name='uniq_enabled_webservices0uuid'),
sa.UniqueConstraint('port', 'board_uuid',
name='uniq_webservices_port_and_board'),
sa.UniqueConstraint('board_uuid', 'port', 'name',
name='uniq_webservices_on_board')
)

View File

@ -163,6 +163,35 @@ class Connection(api.Connection):
query = query.filter(models.Plugin.owner == filters['owner'])
return query
def _add_enabled_webservices_filters(self, query, filters):
if filters is None:
filters = []
if 'project_id' in filters:
query = query.join(models.Board,
models.EnabledWebservice.board_uuid ==
models.Board.uuid)
query = query.filter(
models.Board.project == filters['project_id'])
return query
def _add_webservices_filters(self, query, filters):
# if filters is None:
# filters = []
if 'project_id' in filters:
query = query.join(models.Board,
models.Webservice.board_uuid ==
models.Board.uuid)
query = query.filter(
models.Board.project == filters['project_id'])
if 'board_uuid' in filters:
query = query.filter(
models.Webservice.board_uuid == filters['board_uuid'])
return query
def _add_fleets_filters(self, query, filters):
if filters is None:
filters = []
@ -194,21 +223,8 @@ class Connection(api.Connection):
filters = []
if 'board_uuid' in filters:
query = query.\
query = query. \
filter(models.Port.board_uuid == filters['board_uuid'])
# if 'uuid' in filters:
# query = query.filter(models.Port.uuid == filters['uuid'])
# if 'with_public' in filters and filters['with_public']:
# query = query.filter(
# or_(
# models.Port.owner == filters['owner'],
# models.Port.public == 1)
# )
# else:
# query = query.filter(models.Port.owner == filters['owner'])
# elif 'public' in filters and filters['public']:
# query = query.filter(models.Port.public == 1)
print (str(query))
def _do_update_board(self, board_id, values):
session = get_session()
@ -325,8 +341,8 @@ class Connection(api.Connection):
except NoResultFound:
raise exception.BoardNotFound(board=board_code)
# def get_board_by_port_uuid(self, port_uuid):
# query = model_query(models.Port).filter_by(uuid=port_uuid)
# def get_board_by_port_uuid(self, port_uuid):
# query = model_query(models.Port).filter_by(uuid=port_uuid)
def destroy_board(self, board_id):
session = get_session()
@ -938,7 +954,7 @@ class Connection(api.Connection):
if count == 0:
raise exception.PortNotFound(uuid=uuid)
# FLEET api
# FLEET api
def get_fleet_by_id(self, fleet_id):
query = model_query(models.Fleet).filter_by(id=fleet_id)
@ -1026,3 +1042,152 @@ class Connection(api.Connection):
ref.update(values)
return ref
# WEBSERVICE api
def get_webservice_by_id(self, webservice_id):
query = model_query(models.Webservice).filter_by(id=webservice_id)
try:
return query.one()
except NoResultFound:
raise exception.WebserviceNotFound(webservice=webservice_id)
def get_webservice_by_uuid(self, webservice_uuid):
query = model_query(models.Webservice).filter_by(uuid=webservice_uuid)
try:
return query.one()
except NoResultFound:
raise exception.WebserviceNotFound(webservice=webservice_uuid)
def get_webservice_by_name(self, webservice_name):
query = model_query(models.Webservice).filter_by(name=webservice_name)
try:
return query.one()
except NoResultFound:
raise exception.WebserviceNotFound(webservice=webservice_name)
def destroy_webservice(self, webservice_id):
session = get_session()
with session.begin():
query = model_query(models.Webservice, session=session)
query = add_identity_filter(query, webservice_id)
try:
webservice_ref = query.one()
except NoResultFound:
raise exception.WebserviceNotFound(webservice=webservice_id)
# Get webservice ID, if an UUID was supplied. The ID is
# required for deleting all ports, attached to the webservice.
if uuidutils.is_uuid_like(webservice_id):
webservice_id = webservice_ref['id']
query.delete()
def update_webservice(self, webservice_id, values):
# NOTE(dtantsur): this can lead to very strange errors
if 'uuid' in values:
msg = _("Cannot overwrite UUID for an existing Webservice.")
raise exception.InvalidParameterValue(err=msg)
try:
return self._do_update_webservice(webservice_id, values)
except db_exc.DBDuplicateEntry as e:
if 'name' in e.columns:
raise exception.DuplicateName(name=values['name'])
elif 'uuid' in e.columns:
raise exception.WebserviceAlreadyExists(uuid=values['uuid'])
else:
raise e
def create_webservice(self, values):
# ensure defaults are present for new webservices
if 'uuid' not in values:
values['uuid'] = uuidutils.generate_uuid()
webservice = models.Webservice()
webservice.update(values)
try:
webservice.save()
except db_exc.DBDuplicateEntry:
raise exception.WebserviceAlreadyExists(uuid=values['uuid'])
return webservice
def get_webservice_list(self, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
query = model_query(models.Webservice)
query = self._add_webservices_filters(query, filters)
return _paginate_query(models.Webservice, limit, marker,
sort_key, sort_dir, query)
def _do_update_webservice(self, webservice_id, values):
session = get_session()
with session.begin():
query = model_query(models.Webservice, session=session)
query = add_identity_filter(query, webservice_id)
try:
ref = query.with_lockmode('update').one()
except NoResultFound:
raise exception.WebserviceNotFound(webservice=webservice_id)
ref.update(values)
return ref
# ENABLED_WEBSERIVCE api
def get_enabled_webservice_by_id(self, enabled_webservice_id):
query = model_query(models.EnabledWebservice).filter_by(
id=enabled_webservice_id)
try:
return query.one()
except NoResultFound:
raise exception.EnabledWebserviceNotFound(
enabled_webservice=enabled_webservice_id)
def get_enabled_webservice_by_board_uuid(self, board_uuid):
query = model_query(models.EnabledWebservice).filter_by(
board_uuid=board_uuid)
try:
return query.one()
except NoResultFound:
raise exception.EnabledWebserviceNotFound(
enabled_webservice=board_uuid)
def destroy_enabled_webservice(self, enabled_webservice_id):
session = get_session()
with session.begin():
query = model_query(models.EnabledWebservice, session=session)
query = add_identity_filter(query, enabled_webservice_id)
try:
enabled_webservice_ref = query.one()
except NoResultFound:
raise exception.EnabledWebserviceNotFound(
enabled_webservice=enabled_webservice_id)
# Get enabled_webservice ID, if an UUID was supplied. The ID is
# required for deleting all ports, attached to the enabled_
# webservice.
if uuidutils.is_uuid_like(enabled_webservice_id):
enabled_webservice_id = enabled_webservice_ref['id']
query.delete()
def create_enabled_webservice(self, values):
# ensure defaults are present for new enabled_webservices
if 'uuid' not in values:
values['uuid'] = uuidutils.generate_uuid()
enabled_webservice = models.EnabledWebservice()
enabled_webservice.update(values)
try:
enabled_webservice.save()
except db_exc.DBDuplicateEntry:
raise exception.EnabledWebserviceAlreadyExists(uuid=values['uuid'])
return enabled_webservice
def get_enabled_webservice_list(self, filters=None, limit=None,
marker=None,
sort_key=None, sort_dir=None):
query = model_query(models.EnabledWebservice)
query = self._add_enabled_webservices_filters(query, filters)
return _paginate_query(models.EnabledWebservice, limit, marker,
sort_key, sort_dir, query)

View File

@ -1,7 +1,4 @@
# -*- encoding: utf-8 -*-
#
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@ -282,3 +279,36 @@ class Fleet(Base):
project = Column(String(36))
description = Column(String(300))
extra = Column(JSONEncodedDict)
class Webservice(Base):
"""Represents a webservices."""
__tablename__ = 'webservices'
__table_args__ = (
schema.UniqueConstraint('uuid', name='uniq_webservices0uuid'),
schema.UniqueConstraint('board_uuid', 'port', 'name',
name='uniq_webservices_on_board'),
schema.UniqueConstraint('port', 'board_uuid',
name='uniq_webservices_port_and_board'),
table_args())
id = Column(Integer, primary_key=True)
uuid = Column(String(36))
port = Column(Integer)
name = Column(String(45))
board_uuid = Column(String(36), ForeignKey('boards.uuid'), nullable=True)
secure = Column(Boolean, default=True)
extra = Column(JSONEncodedDict)
class EnabledWebservice(Base):
"""The boards in which webservices are enabled."""
__tablename__ = 'enabled_webservices'
id = Column(Integer, primary_key=True)
board_uuid = Column(String(36), ForeignKey('boards.uuid'), nullable=True)
http_port = Column(Integer)
https_port = Column(Integer)
dns = Column(String(100))
zone = Column(String(100))
extra = Column(JSONEncodedDict)

View File

@ -14,6 +14,7 @@
from iotronic.objects import board
from iotronic.objects import conductor
from iotronic.objects import enabledwebservice
from iotronic.objects import exposedservice
from iotronic.objects import fleet
from iotronic.objects import injectionplugin
@ -23,6 +24,7 @@ from iotronic.objects import port
from iotronic.objects import service
from iotronic.objects import sessionwp
from iotronic.objects import wampagent
from iotronic.objects import webservice
Conductor = conductor.Conductor
Board = board.Board
@ -33,8 +35,10 @@ ExposedService = exposedservice.ExposedService
SessionWP = sessionwp.SessionWP
WampAgent = wampagent.WampAgent
Service = service.Service
Webservice = webservice.Webservice
Port = port.Port
Fleet = fleet.Fleet
EnabledWebservice = enabledwebservice.EnabledWebservice
__all__ = (
Conductor,
@ -47,5 +51,7 @@ __all__ = (
InjectionPlugin,
ExposedService,
Port,
Fleet
Fleet,
Webservice,
EnabledWebservice
)

View File

@ -0,0 +1,186 @@
# coding=utf-8
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import strutils
from oslo_utils import uuidutils
from iotronic.common import exception
from iotronic.db import api as db_api
from iotronic.objects import base
from iotronic.objects import utils as obj_utils
class EnabledWebservice(base.IotronicObject):
# Version 1.0: Initial version
VERSION = '1.0'
dbapi = db_api.get_instance()
fields = {
'id': int,
'board_uuid': obj_utils.str_or_none,
'http_port': int,
'https_port': int,
'dns': obj_utils.str_or_none,
'zone': obj_utils.str_or_none,
'extra': obj_utils.dict_or_none,
}
@staticmethod
def _from_db_object(enabled_webservice, db_enabled_webservice):
"""Converts a database entity to a formal object."""
for field in enabled_webservice.fields:
enabled_webservice[field] = db_enabled_webservice[field]
enabled_webservice.obj_reset_changes()
return enabled_webservice
@base.remotable_classmethod
def get(cls, context, enabled_webservice_id):
"""Find a enabled_webservice based on its id or uuid and return a
Board object.
:param enabled_webservice_id: the id *or* uuid of a enabled_webservice.
:returns: a :class:`Board` object.
"""
if strutils.is_int_like(enabled_webservice_id):
return cls.get_by_id(context, enabled_webservice_id)
elif uuidutils.is_uuid_like(enabled_webservice_id):
return cls.get_by_uuid(context, enabled_webservice_id)
else:
raise exception.InvalidIdentity(identity=enabled_webservice_id)
@base.remotable_classmethod
def get_by_id(cls, context, enabled_webservice_id):
"""Find a enabled_webservice based on its integer id and return a
Board object.
:param enabled_webservice_id: the id of a enabled_webservice.
:returns: a :class:`Board` object.
"""
db_enabled_webservice = cls.dbapi.get_enabled_webservice_by_id(
enabled_webservice_id)
en_webserv = EnabledWebservice._from_db_object(cls(context),
db_enabled_webservice)
return en_webserv
@base.remotable_classmethod
def get_by_board_uuid(cls, context, uuid):
"""Find a enabled_webservice based on uuid and return a Board object.
:param uuid: the uuid of a enabled_webservice.
:returns: a :class:`Board` object.
"""
db_enabled_webservice = cls.dbapi.get_enabled_webservice_by_board_uuid(
uuid)
en_webserv = EnabledWebservice._from_db_object(cls(context),
db_enabled_webservice)
return en_webserv
@base.remotable_classmethod
def list(cls, context, limit=None, marker=None, sort_key=None,
sort_dir=None, filters=None):
"""Return a list of EnabledWebservice objects.
:param context: Security context.
:param limit: maximum number of resources to return in a single result.
:param marker: pagination marker for large data sets.
:param sort_key: column to sort results by.
:param sort_dir: direction to sort. "asc" or "desc".
:param filters: Filters to apply.
:returns: a list of :class:`EnabledWebservice` object.
"""
db_enabled_webservices = cls.dbapi.get_enabled_webservice_list(
filters=filters,
limit=limit,
marker=marker,
sort_key=sort_key,
sort_dir=sort_dir)
return [EnabledWebservice._from_db_object(cls(context), obj)
for obj in db_enabled_webservices]
@base.remotable
def create(self, context=None):
"""Create a EnabledWebservice record in the DB.
Column-wise updates will be made based on the result of
self.what_changed(). If target_power_state is provided,
it will be checked against the in-database copy of the
enabled_webservice before updates are made.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: EnabledWebservice(context)
"""
values = self.obj_get_changes()
db_enabled_webservice = self.dbapi.create_enabled_webservice(values)
self._from_db_object(self, db_enabled_webservice)
@base.remotable
def destroy(self, context=None):
"""Delete the EnabledWebservice from the DB.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: EnabledWebservice(context)
"""
self.dbapi.destroy_enabled_webservice(self.id)
self.obj_reset_changes()
@base.remotable
def save(self, context=None):
"""Save updates to this EnabledWebservice.
Column-wise updates will be made based on the result of
self.what_changed(). If target_power_state is provided,
it will be checked against the in-database copy of the
enabled_webservice before updates are made.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: EnabledWebservice(context)
"""
updates = self.obj_get_changes()
self.dbapi.update_enabled_webservice(self.uuid, updates)
self.obj_reset_changes()
@base.remotable
def refresh(self, context=None):
"""Refresh the object by re-fetching from the DB.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: EnabledWebservice(context)
"""
current = self.__class__.get_by_uuid(self._context, self.uuid)
for field in self.fields:
if (hasattr(
self, base.get_attrname(field))
and self[field] != current[field]):
self[field] = current[field]

View File

@ -0,0 +1,190 @@
# coding=utf-8
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import strutils
from oslo_utils import uuidutils
from iotronic.common import exception
from iotronic.db import api as db_api
from iotronic.objects import base
from iotronic.objects import utils as obj_utils
class Webservice(base.IotronicObject):
# Version 1.0: Initial version
VERSION = '1.0'
dbapi = db_api.get_instance()
fields = {
'id': int,
'uuid': obj_utils.str_or_none,
'port': int,
'name': obj_utils.str_or_none,
'board_uuid': obj_utils.str_or_none,
'secure': bool,
'extra': obj_utils.dict_or_none,
}
@staticmethod
def _from_db_object(webservice, db_webservice):
"""Converts a database entity to a formal object."""
for field in webservice.fields:
webservice[field] = db_webservice[field]
webservice.obj_reset_changes()
return webservice
@base.remotable_classmethod
def get(cls, context, webservice_id):
"""Find a webservice based on its id or uuid and return a Board object.
:param webservice_id: the id *or* uuid of a webservice.
:returns: a :class:`Board` object.
"""
if strutils.is_int_like(webservice_id):
return cls.get_by_id(context, webservice_id)
elif uuidutils.is_uuid_like(webservice_id):
return cls.get_by_uuid(context, webservice_id)
else:
raise exception.InvalidIdentity(identity=webservice_id)
@base.remotable_classmethod
def get_by_id(cls, context, webservice_id):
"""Find a webservice based on its integer id and return a Board object.
:param webservice_id: the id of a webservice.
:returns: a :class:`Board` object.
"""
db_webservice = cls.dbapi.get_webservice_by_id(webservice_id)
webservice = Webservice._from_db_object(cls(context), db_webservice)
return webservice
@base.remotable_classmethod
def get_by_uuid(cls, context, uuid):
"""Find a webservice based on uuid and return a Board object.
:param uuid: the uuid of a webservice.
:returns: a :class:`Board` object.
"""
db_webservice = cls.dbapi.get_webservice_by_uuid(uuid)
webservice = Webservice._from_db_object(cls(context), db_webservice)
return webservice
@base.remotable_classmethod
def get_by_name(cls, context, name):
"""Find a webservice based on name and return a Board object.
:param name: the logical name of a webservice.
:returns: a :class:`Board` object.
"""
db_webservice = cls.dbapi.get_webservice_by_name(name)
webservice = Webservice._from_db_object(cls(context), db_webservice)
return webservice
@base.remotable_classmethod
def list(cls, context, limit=None, marker=None, sort_key=None,
sort_dir=None, filters=None):
"""Return a list of Webservice objects.
:param context: Security context.
:param limit: maximum number of resources to return in a single result.
:param marker: pagination marker for large data sets.
:param sort_key: column to sort results by.
:param sort_dir: direction to sort. "asc" or "desc".
:param filters: Filters to apply.
:returns: a list of :class:`Webservice` object.
"""
db_webservices = cls.dbapi.get_webservice_list(filters=filters,
limit=limit,
marker=marker,
sort_key=sort_key,
sort_dir=sort_dir)
return [Webservice._from_db_object(cls(context), obj)
for obj in db_webservices]
@base.remotable
def create(self, context=None):
"""Create a Webservice record in the DB.
Column-wise updates will be made based on the result of
self.what_changed(). If target_power_state is provided,
it will be checked against the in-database copy of the
webservice before updates are made.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Webservice(context)
"""
values = self.obj_get_changes()
db_webservice = self.dbapi.create_webservice(values)
self._from_db_object(self, db_webservice)
@base.remotable
def destroy(self, context=None):
"""Delete the Webservice from the DB.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Webservice(context)
"""
self.dbapi.destroy_webservice(self.uuid)
self.obj_reset_changes()
@base.remotable
def save(self, context=None):
"""Save updates to this Webservice.
Column-wise updates will be made based on the result of
self.what_changed(). If target_power_state is provided,
it will be checked against the in-database copy of the
webservice before updates are made.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Webservice(context)
"""
updates = self.obj_get_changes()
self.dbapi.update_webservice(self.uuid, updates)
self.obj_reset_changes()
@base.remotable
def refresh(self, context=None):
"""Refresh the object by re-fetching from the DB.
:param context: Security context. NOTE: This should only
be used internally by the indirection_api.
Unfortunately, RPC requires context as the first
argument, even though we don't use it.
A context should be set when instantiating the
object, e.g.: Webservice(context)
"""
current = self.__class__.get_by_uuid(self._context, self.uuid)
for field in self.fields:
if (hasattr(
self, base.get_attrname(field))
and self[field] != current[field]):
self[field] = current[field]

View File

@ -27,6 +27,7 @@ from oslo_log import log as logging
import oslo_messaging
from oslo_messaging.rpc import dispatcher
import importlib
from threading import Thread
import ssl
@ -74,8 +75,15 @@ wamp_opts = [
]
proxy_opts = [
cfg.StrOpt('proxy',
choices=[('nginx', _('nginx proxy')), ],
help=_('Proxy for webservices')),
]
CONF = cfg.CONF
cfg.CONF.register_opts(service_opts)
cfg.CONF.register_opts(proxy_opts)
CONF.register_opts(wamp_opts, 'wamp')
txaio.start_logging(level="info")
@ -94,11 +102,6 @@ async def wamp_request(kwarg):
# OSLO ENDPOINT
class WampEndpoint(object):
def __init__(self, agent_uuid):
setattr(self, agent_uuid + '.s4t_invoke_wamp', self.s4t_invoke_wamp)
setattr(self, agent_uuid + '.create_tap_interface',
self.create_tap_interface)
def s4t_invoke_wamp(self, ctx, **kwarg):
LOG.debug("CONDUCTOR sent me: " + kwarg['wamp_rpc_call'])
@ -107,6 +110,14 @@ class WampEndpoint(object):
return r.result()
class AgentEndpoint(object):
# used for testing
def echo(self, ctx, text):
LOG.debug("ECHO of " + text)
return text
def create_tap_interface(self, ctx, port_uuid, tcp_port):
time.sleep(12)
LOG.debug('Creating tap interface on the wamp agent host')
@ -121,18 +132,20 @@ class WampEndpoint(object):
class RPCServer(Thread):
def __init__(self):
# AMQP CONFIG
proxy = importlib.import_module("iotronic.wamp.proxies." + CONF.proxy)
endpoints = [
WampEndpoint(AGENT_HOST),
WampEndpoint(),
AgentEndpoint(),
proxy.ProxyManager()
]
Thread.__init__(self)
transport = oslo_messaging.get_transport(CONF)
target = oslo_messaging.Target(topic=AGENT_HOST + '.s4t_invoke_wamp',
server='server1')
target1 = oslo_messaging.Target(topic=AGENT_HOST +
'.create_tap_interface',
server='server1')
target = oslo_messaging.Target(topic='s4t',
server=AGENT_HOST)
access_policy = dispatcher.DefaultRPCAccessPolicy
self.server = oslo_messaging.get_rpc_server(
@ -140,20 +153,13 @@ class RPCServer(Thread):
endpoints, executor='threading',
access_policy=access_policy)
self.server1 = oslo_messaging.get_rpc_server(
transport, target1,
endpoints, executor='threading',
access_policy=access_policy)
def run(self):
LOG.info("Starting AMQP server... ")
self.server.start()
self.server1.start()
def stop(self):
LOG.info("Stopping AMQP server... ")
self.server.stop()
self.server1.stop()
LOG.info("AMQP server stopped. ")
@ -223,6 +229,10 @@ class WampManager(object):
AGENT_HOST + u'.stack4things.connection')
session.register(fun.echo,
AGENT_HOST + u'.stack4things.echo')
session.register(fun.alive,
AGENT_HOST + u'.stack4things.alive')
session.register(fun.wamp_alive,
AGENT_HOST + u'.stack4things.wamp_alive')
LOG.debug("procedure registered")
except Exception as e:

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
from iotronic.common import rpc
from iotronic.common import states
from iotronic.conductor import rpcapi
@ -45,6 +46,19 @@ def echo(data):
return data
def wamp_alive(board_uuid, board_name):
LOG.debug("Alive board: %s (%s)", board_uuid, board_name)
return "Iotronic alive @ " + datetime.now().strftime(
'%Y-%m-%dT%H:%M:%S.%f')
# to be removed
def alive():
LOG.debug("Alive")
return "Iotronic alive @ " + datetime.now().strftime(
'%Y-%m-%dT%H:%M:%S.%f')
def update_sessions(session_list):
session_list = set(session_list)
list_from_db = objects.SessionWP.valid_list(ctxt)

View File

View File

@ -0,0 +1,97 @@
# Copyright 2017 MDSLAB - University of Messina
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from iotronic.wamp.proxies.proxy import Proxy
from oslo_config import cfg
from oslo_log import log as logging
from subprocess import call
LOG = logging.getLogger(__name__)
nginx_opts = [
cfg.StrOpt('nginx_path',
default='/etc/nginx/conf.d/iotronic',
help=('Default Nginx Path')),
cfg.StrOpt('dns_zone',
default='openstack.iotronic',
help=('Default zone')),
]
CONF = cfg.CONF
CONF.register_opts(nginx_opts, 'nginx')
def save_map(board, dns):
fp = CONF.nginx.nginx_path + "/maps/map_" + board
with open(fp, "w") as text_file:
text_file.write("~" + board + "." + dns + " " + board + ";")
def save_upstream(board, https_port):
fp = CONF.nginx.nginx_path + "/upstreams/upstream_" + board
string = '''upstream {0} {{
server localhost:{1} max_fails=3 fail_timeout=10s;
}}
'''.format(board, https_port)
with open(fp, "w") as text_file:
text_file.write("%s" % string)
def save_server(board, http_port, dns):
fp = CONF.nginx.nginx_path + "/servers/" + board
string = '''server {{
listen 80;
server_name .{0}.{2};
location / {{
proxy_pass http://localhost:{1};
}}
}}
'''.format(board, http_port, dns)
with open(fp, "w") as text_file:
text_file.write("%s" % string)
def remove(board):
call(["rm",
CONF.nginx.nginx_path + "/servers/" + board,
CONF.nginx.nginx_path + "/upstreams/upstream_" + board,
CONF.nginx.nginx_path + "/maps/map_" + board
])
class ProxyManager(Proxy):
def __init__(self):
self.dns = CONF.nginx.dns_zone
super(ProxyManager, self).__init__("nginx")
def reload_proxy(self, ctx):
call(["nginx", "-s", "reload"])
def enable_webservice(self, ctx, board, https_port, http_port):
LOG.debug(
'Enabling WebService with ports %s for http and %s for https '
'on board %s', http_port, https_port, board)
save_map(board, self.dns)
save_upstream(board, https_port)
save_server(board, http_port, self.dns)
def disable_webservice(self, ctx, board):
LOG.debug('Disabling WebService on board %s',
board)
remove(board)

View File

@ -0,0 +1,34 @@
# Copyright 2011 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class Proxy(object):
"""Base class for proxies supported by Iotornic.
"""
def __init__(self, proxy_type):
self.proxy_type = proxy_type
# def enable_webservice(self,board,https_port,http_port):
# pass

View File

@ -15,6 +15,7 @@ SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT
keystonemiddleware>=4.17.0 # Apache-2.0
autobahn>=0.10.1 # MIT License
python-neutronclient>=6.7.0 # Apache-2.0
python-designateclient>=2.11.0 # Apache-2.0
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
PyMySQL>=0.7.6 # MIT License
osprofiler>=1.5.0 # Apache-2.0