Add cyborg-api command

Change-Id: I392ad0383d5d8299db407df79bd60adbfbd428cd
This commit is contained in:
zhuli 2017-07-29 11:51:22 +08:00
parent 9fa1a7183c
commit c7b24fda7f
14 changed files with 550 additions and 0 deletions

19
cyborg/cmd/__init__.py Normal file
View File

@ -0,0 +1,19 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_i18n as i18n
i18n.install('cyborg')

36
cyborg/cmd/api.py Normal file
View File

@ -0,0 +1,36 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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.
"""The Cyborg Service API."""
import sys
from oslo_config import cfg
from cyborg.common import service as cyborg_service
CONF = cfg.CONF
def main():
# Parse config file and command line options, then start logging
cyborg_service.prepare_service(sys.argv)
# Build and start the WSGI app
launcher = cyborg_service.process_launcher()
server = cyborg_service.WSGIService('cyborg_api', CONF.api.enable_ssl_api)
launcher.launch_service(server, workers=server.workers)
launcher.wait()

View File

26
cyborg/common/config.py Normal file
View File

@ -0,0 +1,26 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_config import cfg
from cyborg import version
def parse_args(argv, default_config_files=None):
version_string = version.version_info.release_string()
cfg.CONF(argv[1:],
project='cyborg',
version=version_string,
default_config_files=default_config_files)

View File

@ -0,0 +1,90 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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.
"""Cyborg base exception handling.
SHOULD include dedicated exception logging.
"""
from oslo_log import log
import six
from six.moves import http_client
from cyborg.common.i18n import _
from cyborg.conf import CONF
LOG = log.getLogger(__name__)
class CyborgException(Exception):
"""Base Cyborg Exception
To correctly use this class, inherit from it and define
a '_msg_fmt' property. That message will get printf'd
with the keyword arguments provided to the constructor.
If you need to access the message from an exception you should use
six.text_type(exc)
"""
_msg_fmt = _("An unknown exception occurred.")
code = http_client.INTERNAL_SERVER_ERROR
headers = {}
safe = False
def __init__(self, message=None, **kwargs):
self.kwargs = kwargs
if 'code' not in self.kwargs:
try:
self.kwargs['code'] = self.code
except AttributeError:
pass
if not message:
try:
message = self._msg_fmt % kwargs
except Exception:
# kwargs doesn't match a variable in self._msg_fmt
# log the issue and the kwargs
LOG.exception('Exception in string format operation')
for name, value in kwargs.items():
LOG.error("%s: %s" % (name, value))
if CONF.fatal_exception_format_errors:
raise
else:
# at least get the core self._msg_fmt out if something
# happened
message = self._msg_fmt
super(CyborgException, self).__init__(message)
def __str__(self):
"""Encode to utf-8 then wsme api can consume it as well."""
if not six.PY3:
return unicode(self.args[0]).encode('utf-8')
return self.args[0]
def __unicode__(self):
"""Return a unicode representation of the exception message."""
return unicode(self.args[0])
class ConfigInvalid(CyborgException):
_msg_fmt = _("Invalid configuration file. %(error_msg)s")

22
cyborg/common/i18n.py Normal file
View File

@ -0,0 +1,22 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_i18n as i18n
_translators = i18n.TranslatorFactory(domain='cyborg')
# The primary translation function using the well-known name "_"
_ = _translators.primary

121
cyborg/common/rpc.py Normal file
View File

@ -0,0 +1,121 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_config import cfg
from oslo_context import context as cyborg_context
import oslo_messaging as messaging
from oslo_messaging.rpc import dispatcher
from cyborg.common import exception
CONF = cfg.CONF
TRANSPORT = None
NOTIFICATION_TRANSPORT = None
NOTIFIER = None
ALLOWED_EXMODS = [
exception.__name__,
]
EXTRA_EXMODS = []
def init(conf):
global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER
exmods = get_allowed_exmods()
TRANSPORT = messaging.get_rpc_transport(conf,
allowed_remote_exmods=exmods)
NOTIFICATION_TRANSPORT = messaging.get_notification_transport(
conf,
allowed_remote_exmods=exmods)
serializer = RequestContextSerializer(messaging.JsonPayloadSerializer())
NOTIFIER = messaging.Notifier(NOTIFICATION_TRANSPORT,
serializer=serializer,
topics=['notifications'])
def cleanup():
global TRANSPORT, NOTIFICATION_TRANSPORT, NOTIFIER
assert TRANSPORT is not None
assert NOTIFICATION_TRANSPORT is not None
assert NOTIFIER is not None
TRANSPORT.cleanup()
NOTIFICATION_TRANSPORT.cleanup()
TRANSPORT = NOTIFICATION_TRANSPORT = NOTIFIER = None
def set_defaults(control_exchange):
messaging.set_transport_defaults(control_exchange)
def add_extra_exmods(*args):
EXTRA_EXMODS.extend(args)
def clear_extra_exmods():
del EXTRA_EXMODS[:]
def get_allowed_exmods():
return ALLOWED_EXMODS + EXTRA_EXMODS
class RequestContextSerializer(messaging.Serializer):
def __init__(self, base):
self._base = base
def serialize_entity(self, context, entity):
if not self._base:
return entity
return self._base.serialize_entity(context, entity)
def deserialize_entity(self, context, entity):
if not self._base:
return entity
return self._base.deserialize_entity(context, entity)
def serialize_context(self, context):
return context.to_dict()
def deserialize_context(self, context):
return cyborg_context.RequestContext.from_dict(context)
def get_client(target, version_cap=None, serializer=None):
assert TRANSPORT is not None
serializer = RequestContextSerializer(serializer)
return messaging.RPCClient(TRANSPORT,
target,
version_cap=version_cap,
serializer=serializer)
def get_server(target, endpoints, serializer=None):
assert TRANSPORT is not None
access_policy = dispatcher.DefaultRPCAccessPolicy
serializer = RequestContextSerializer(serializer)
return messaging.get_rpc_server(TRANSPORT,
target,
endpoints,
executor='eventlet',
serializer=serializer,
access_policy=access_policy)
def get_notifier(service=None, host=None, publisher_id=None):
assert NOTIFIER is not None
if not publisher_id:
publisher_id = "%s.%s" % (service, host or CONF.host)
return NOTIFIER.prepare(publisher_id=publisher_id)

98
cyborg/common/service.py Normal file
View File

@ -0,0 +1,98 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_concurrency import processutils
from oslo_log import log
from oslo_service import service
from oslo_service import wsgi
from cyborg.api import app
from cyborg.common import config
from cyborg.common import exception
from cyborg.common.i18n import _
from cyborg.common import rpc
from cyborg.conf import CONF
LOG = log.getLogger(__name__)
def prepare_service(argv=None):
log.register_options(CONF)
log.set_defaults()
rpc.set_defaults(control_exchange='cyborg')
argv = argv or []
config.parse_args(argv)
log.setup(CONF, 'cyborg')
rpc.init(CONF)
def process_launcher():
return service.ProcessLauncher(CONF)
class WSGIService(service.ServiceBase):
"""Provides ability to launch cyborg API from wsgi app."""
def __init__(self, name, use_ssl=False):
"""Initialize, but do not start the WSGI server.
:param name: The name of the WSGI server given to the loader.
:param use_ssl: Wraps the socket in an SSL context if True.
:returns: None
"""
self.name = name
self.app = app.setup_app()
self.workers = (CONF.api.api_workers or
processutils.get_worker_count())
if self.workers and self.workers < 1:
raise exception.ConfigInvalid(
_("api_workers value of %d is invalid, "
"must be greater than 0.") % self.workers)
self.server = wsgi.Server(CONF, self.name, self.app,
host=CONF.api.host_ip,
port=CONF.api.port,
use_ssl=use_ssl)
def start(self):
"""Start serving this service using loaded configuration.
:returns: None
"""
self.server.start()
def stop(self):
"""Stop serving this API.
:returns: None
"""
self.server.stop()
def wait(self):
"""Wait for the service to stop serving this API.
:returns: None
"""
self.server.wait()
def reset(self):
"""Reset server greenpool size to default.
:returns: None
"""
self.server.reset()

25
cyborg/conf/__init__.py Normal file
View File

@ -0,0 +1,25 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_config import cfg
from cyborg.conf import api
from cyborg.conf import default
CONF = cfg.CONF
api.register_opts(CONF)
default.register_opts(CONF)

49
cyborg/conf/api.py Normal file
View File

@ -0,0 +1,49 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_config import cfg
from cyborg.common.i18n import _
opts = [
cfg.HostAddressOpt('host_ip',
default='0.0.0.0',
help=_('The IP address on which cyborg-api listens.')),
cfg.PortOpt('port',
default=6666,
help=_('The TCP port on which cyborg-api listens.')),
cfg.IntOpt('api_workers',
help=_('Number of workers for OpenStack Cyborg API service. '
'The default is equal to the number of CPUs available '
'if that can be determined, else a default worker '
'count of 1 is returned.')),
cfg.BoolOpt('enable_ssl_api',
default=False,
help=_("Enable the integrated stand-alone API to service "
"requests via HTTPS instead of HTTP. If there is a "
"front-end service performing HTTPS offloading from "
"the service, this option should be False; note, you "
"will want to change public API endpoint to represent "
"SSL termination URL with 'public_endpoint' option.")),
]
opt_group = cfg.OptGroup(name='api',
title='Options for the cyborg-api service')
def register_opts(conf):
conf.register_group(opt_group)
conf.register_opts(opts, group=opt_group)

32
cyborg/conf/default.py Normal file
View File

@ -0,0 +1,32 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 oslo_config import cfg
from cyborg.common.i18n import _
exc_log_opts = [
cfg.BoolOpt('fatal_exception_format_errors',
default=False,
help=_('Used if there is a formatting error when generating '
'an exception message (a programming error). If True, '
'raise an exception; if False, use the unformatted '
'message.')),
]
def register_opts(conf):
conf.register_opts(exc_log_opts)

19
cyborg/version.py Normal file
View File

@ -0,0 +1,19 @@
# Copyright 2017 Huawei Technologies Co.,LTD.
# 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 pbr.version
version_info = pbr.version.VersionInfo('cyborg')

View File

@ -6,3 +6,12 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
pecan!=1.0.2,!=1.0.3,!=1.0.4,!=1.2,>=1.0.0 # BSD
WSME>=0.8 # MIT
six>=1.9.0 # MIT
eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0
oslo.context>=2.14.0 # Apache-2.0
oslo.messaging!=5.25.0,>=5.24.2 # Apache-2.0
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.service>=1.10.0 # Apache-2.0
oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0

View File

@ -23,6 +23,10 @@ classifier =
packages =
cyborg
[entry_points]
console_scripts =
cyborg-api = cyborg.cmd.api:main
[build_sphinx]
source-dir = doc/source
build-dir = doc/build