Migrate to flask
This commit migrate the valence code to flask and aslo removed rabbitmq. Change-Id: I70234515960e7e2106c5208ced8defc760a4531e
This commit is contained in:
parent
cac939f819
commit
da241b473f
32
README.rst
32
README.rst
@ -16,45 +16,35 @@ Download and Installation
|
||||
|
||||
The following steps capture how to install valence. All installation steps require super user permissions.
|
||||
|
||||
********************
|
||||
*******************************************
|
||||
Valence installation
|
||||
********************
|
||||
*******************************************
|
||||
|
||||
1. Install software dependencies
|
||||
|
||||
``$ sudo apt-get install git python-pip rabbitmq-server libyaml-0-2 python-dev``
|
||||
``$ sudo apt-get install git python-pip``
|
||||
|
||||
2. Configure RabbitMq Server
|
||||
2. Clone the Valence code from git repo.
|
||||
|
||||
``$ sudo rabbitmqctl add_user valence valence #use this username/pwd in valence.conf``
|
||||
``$ git clone https://git.openstack.org/openstack/rsc``
|
||||
|
||||
``$ sudo rabbitmqctl set_user_tags valence administrator``
|
||||
3. Install all necessary software pre-requisites using the pip requirements file.
|
||||
|
||||
``$ sudo rabbitmqctl set_permissions valence ".*" ".*" ".*"``
|
||||
|
||||
3. Clone the Valence code from git repo and change the directory to root Valence folder.
|
||||
|
||||
4. Install all necessary software pre-requisites using the pip requirements file.
|
||||
|
||||
``$ sudo -E pip install -r requirements.txt``
|
||||
``$ pip install -r requirements.txt``
|
||||
|
||||
5. Execute the 'install_valence.sh' file the Valence root directory.
|
||||
|
||||
``$ ./install_valence.sh``
|
||||
``$ sudo bash install_valence.sh``
|
||||
|
||||
6. Check the values in valence.conf located at /etc/valence/valence.conf
|
||||
|
||||
``set the ip/credentials of podm for which this Valence will interact``
|
||||
|
||||
``set the rabbitmq user/password to the one given above(Step 2)``
|
||||
7. Check the PYTHON_HOME and other variables in /etc/init/valence.conf
|
||||
|
||||
7. Check the values in /etc/init/valence-api.conf, /etc/init/valence-controller.conf
|
||||
8. Start valence service
|
||||
|
||||
8. Start api and controller services
|
||||
|
||||
``$ sudo service valence-api start``
|
||||
|
||||
``$ sudo service valence-controller start``
|
||||
``$ sudo service valence start``
|
||||
|
||||
9. Logs are located at /var/logs/valence/
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
description "Valence Controller server"
|
||||
|
||||
start on runlevel [2345]
|
||||
stop on runlevel [!2345]
|
||||
|
||||
env PYTHON_HOME=PYHOME
|
||||
|
||||
exec start-stop-daemon --start --verbose --chuid ${CHUID} \
|
||||
--name valence-controller \
|
||||
--exec /usr/local/bin/valence-controller -- \
|
||||
--log-file=/var/log/valence/valence-controller.log
|
||||
|
||||
respawn
|
||||
|
@ -1,4 +1,4 @@
|
||||
description "Valence API server"
|
||||
description "Valence server"
|
||||
|
||||
start on runlevel [2345]
|
||||
stop on runlevel [!2345]
|
||||
@ -7,9 +7,8 @@ env PYTHON_HOME=PYHOME
|
||||
|
||||
# change the chuid to match yours
|
||||
exec start-stop-daemon --start --verbose --chuid ${CHUID} \
|
||||
--name valence-api \
|
||||
--exec /usr/local/bin/valence-api -- \
|
||||
--log-file=/var/log/valence/valence-api.log
|
||||
--name valence \
|
||||
--exec $PYTHON_HOME/valence -- \
|
||||
|
||||
respawn
|
||||
|
@ -1,37 +1,20 @@
|
||||
[DEFAULT]
|
||||
# Show more verbose log output (sets INFO log level output)
|
||||
verbose = True
|
||||
#LOG Levels - debug, info, warning, error, critical
|
||||
log_level= debug
|
||||
|
||||
# Show debugging output in logs (sets DEBUG log level output)
|
||||
debug = False
|
||||
|
||||
auth_strategy=noauth
|
||||
#Server log settings
|
||||
debug=True
|
||||
|
||||
# Log to this file. Make sure the user running rsc has
|
||||
# permissions to write to this file!
|
||||
log_file=/var/log/valence/valence.log
|
||||
|
||||
|
||||
log_dir=/var/log/valence
|
||||
rpc_response_timeout = 300
|
||||
|
||||
|
||||
[api]
|
||||
#address to bind the server to
|
||||
#address and port the server binds too
|
||||
bind_host = 0.0.0.0
|
||||
|
||||
# Port the bind the server to
|
||||
bind_port = 8181
|
||||
|
||||
[oslo_messaging_rabbit]
|
||||
rabbit_host = localhost
|
||||
rabbit_port = 5672
|
||||
rabbit_userid = valence
|
||||
rabbit_password = valence
|
||||
|
||||
[podm]
|
||||
#url=http://10.223.197.204
|
||||
url=http://<ip address>
|
||||
user=<user>
|
||||
password=<password>
|
||||
|
||||
user=<podm user>
|
||||
password=<podm admin>
|
||||
|
||||
|
@ -4,36 +4,30 @@
|
||||
#author :Intel Corporation
|
||||
#date :17-10-2016
|
||||
#version :0.1
|
||||
#usage :bash install_valence.sh
|
||||
#usage :sudo -E bash install_valence.sh
|
||||
#notes :Run this script as sudo user and not as root.
|
||||
# This script is needed still valence is packaged in to .deb/.rpm
|
||||
#==============================================================================
|
||||
|
||||
install_log=install_valence.log
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
cd $DIR
|
||||
echo "Current directory: $DIR" >> $install_log
|
||||
if [ "$USER" != 'root' ]; then
|
||||
echo "You must be root to install."
|
||||
exit
|
||||
fi
|
||||
PYHOME=$(python -c "import site; print site.getsitepackages()[0]")
|
||||
|
||||
PYHOME="/usr/local/bin"
|
||||
echo "Detected PYTHON HOME: $PYHOME" >> $install_log
|
||||
|
||||
# Copy the config files
|
||||
cp $DIR/doc/source/init/valence-api.conf /tmp/valence-api.conf
|
||||
sed -i s/\${CHUID}/$USER/ /tmp/valence-api.conf
|
||||
#Use alternate sed delimiter because path will
|
||||
#have /
|
||||
sed -i "s#PYHOME#$PYHOME#" /tmp/valence-api.conf
|
||||
mv /tmp/valence-api.conf /etc/init/valence-api.conf
|
||||
echo "Setting up valence-api config" >> $install_log
|
||||
|
||||
cp $DIR/doc/source/init/valence-controller.conf /tmp/valence-controller.conf
|
||||
sed -i s/\${CHUID}/$USER/ /tmp/valence-controller.conf
|
||||
#Use alternate sed delimiter because path will
|
||||
#have /
|
||||
sed -i "s#PYHOME#$PYHOME#" /tmp/valence-controller.conf
|
||||
mv /tmp/valence-controller.conf /etc/init/valence-controller.conf
|
||||
echo "Setting up valence-controller config" >> $install_log
|
||||
echo "Setting up valence config" >> $install_log
|
||||
sed s/\${CHUID}/$USER/ $DIR/doc/source/init/valence.conf > /tmp/valence.conf
|
||||
#Use alternate sed delimiter because path will have /
|
||||
sed -i "s#PYHOME#$PYHOME#" /tmp/valence.conf
|
||||
mv /tmp/valence.conf /etc/init/valence.conf
|
||||
|
||||
# create conf directory for valence
|
||||
mkdir /etc/valence
|
||||
@ -52,5 +46,4 @@ if [ $? -ne 0 ]; then
|
||||
fi
|
||||
|
||||
echo "Installation Completed"
|
||||
echo "To start api : sudo service valence-api start"
|
||||
echo "To start controller : sudo service valence-controller start"
|
||||
echo "To start valence : sudo service valence start"
|
||||
|
@ -1,41 +1,14 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=1.6
|
||||
Babel>=2.3.4
|
||||
Paste>=2.0.3
|
||||
PasteDeploy>=1.5.2
|
||||
PyYAML>=3.11
|
||||
WebOb>=1.6.1
|
||||
amqp<=2.0
|
||||
anyjson>=0.3.3
|
||||
argparse>=1.2.1
|
||||
contextlib2>=0.5.3
|
||||
eventlet>=0.19.0
|
||||
greenlet>=0.4.10
|
||||
kombu>=3.0.35
|
||||
logutils>=0.3.3
|
||||
monotonic>=1.1
|
||||
netaddr>=0.7.18
|
||||
netifaces>=0.10.4
|
||||
oslo.concurrency>=3.10.0
|
||||
oslo.config>=3.11.0
|
||||
oslo.context>=2.5.0
|
||||
oslo.i18n>=3.7.0
|
||||
oslo.log>=3.10.0
|
||||
oslo.messaging>=5.4.0
|
||||
oslo.middleware>=3.13.0
|
||||
oslo.reports>=1.11.0
|
||||
oslo.serialization>=2.9.0
|
||||
oslo.service>=1.12.0
|
||||
oslo.utils>=3.13.0
|
||||
oslo.versionedobjects>=1.12.0
|
||||
pecan>=1.1.1
|
||||
requests>=2.10.0
|
||||
six>=1.10.0
|
||||
stevedore>=1.15.0
|
||||
waitress>=0.9.0
|
||||
wrapt>=1.10.8
|
||||
wsgiref>=0.1.2
|
||||
|
||||
aniso8601==1.2.0
|
||||
click==6.6
|
||||
Flask==0.11.1
|
||||
Flask-Cors==3.0.2
|
||||
Flask-RESTful==0.3.5
|
||||
itsdangerous==0.24
|
||||
Jinja2==2.8
|
||||
MarkupSafe==0.23
|
||||
python-dateutil==2.5.3
|
||||
pytz==2016.7
|
||||
requests==2.11.1
|
||||
six==1.10.0
|
||||
Werkzeug==0.11.11
|
||||
|
@ -52,8 +52,4 @@ source-dir = releasenotes/source
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
valence-api = valence.cmd.api:main
|
||||
valence-controller = valence.cmd.controller:main
|
||||
|
||||
oslo.config.opts =
|
||||
valence = valence.api.config:list_opts
|
||||
valence = valence.run:main
|
||||
|
@ -10,51 +10,31 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_service import service
|
||||
from pecan import configuration
|
||||
from pecan import make_app
|
||||
from valence.api import hooks
|
||||
from flask import Flask
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from valence import config as cfg
|
||||
|
||||
_app = None
|
||||
|
||||
|
||||
def setup_app(*args, **kwargs):
|
||||
config = {
|
||||
'server': {
|
||||
'host': cfg.CONF.api.bind_port,
|
||||
'port': cfg.CONF.api.bind_host
|
||||
},
|
||||
'app': {
|
||||
'root': 'valence.api.controllers.root.RootController',
|
||||
'modules': ['valence.api'],
|
||||
'errors': {
|
||||
400: '/error',
|
||||
'__force_dict__': True
|
||||
}
|
||||
}
|
||||
}
|
||||
pecan_config = configuration.conf_from_dict(config)
|
||||
def setup_app():
|
||||
"""Return Flask application"""
|
||||
app = Flask(cfg.PROJECT_NAME)
|
||||
app.url_map.strict_slashes = False
|
||||
|
||||
app_hooks = [hooks.CORSHook()]
|
||||
|
||||
app = make_app(
|
||||
pecan_config.app.root,
|
||||
hooks=app_hooks,
|
||||
force_canonical=False,
|
||||
logging=getattr(config, 'logging', {})
|
||||
)
|
||||
# Configure logging
|
||||
handler = RotatingFileHandler(cfg.log_file, maxBytes=10000, backupCount=1)
|
||||
handler.setLevel(cfg.log_level)
|
||||
formatter = logging.Formatter(cfg.log_format)
|
||||
handler.setFormatter(formatter)
|
||||
app.logger.setLevel(cfg.log_level)
|
||||
app.logger.addHandler(handler)
|
||||
return app
|
||||
|
||||
|
||||
_launcher = None
|
||||
|
||||
|
||||
def serve(api_service, conf, workers=1):
|
||||
global _launcher
|
||||
if _launcher:
|
||||
raise RuntimeError('serve() can only be called once')
|
||||
|
||||
_launcher = service.launch(conf, api_service, workers=workers)
|
||||
|
||||
|
||||
def wait():
|
||||
_launcher.wait()
|
||||
def get_app():
|
||||
global _app
|
||||
if not _app:
|
||||
_app = setup_app()
|
||||
return _app
|
||||
|
@ -1,66 +0,0 @@
|
||||
# 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_log import log as logging
|
||||
import sys
|
||||
from valence.common import rpc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
common_opts = [
|
||||
cfg.StrOpt('auth_strategy', default='noauth',
|
||||
help=("The type of authentication to use")),
|
||||
cfg.BoolOpt('allow_pagination', default=False,
|
||||
help=("Allow the usage of the pagination")),
|
||||
cfg.BoolOpt('allow_sorting', default=False,
|
||||
help=("Allow the usage of the sorting")),
|
||||
cfg.StrOpt('pagination_max_limit', default="-1",
|
||||
help=("The maximum number of items returned in a single "
|
||||
"response, value was 'infinite' or negative integer "
|
||||
"means no limit")),
|
||||
]
|
||||
|
||||
api_opts = [
|
||||
cfg.StrOpt('bind_host', default='0.0.0.0',
|
||||
help=("The host IP to bind to")),
|
||||
cfg.IntOpt('bind_port', default=8181,
|
||||
help=("The port to bind to")),
|
||||
cfg.IntOpt('api_workers', default=2,
|
||||
help=("number of api workers"))
|
||||
]
|
||||
|
||||
|
||||
def init(args, **kwargs):
|
||||
# Register the configuration options
|
||||
api_conf_group = cfg.OptGroup(name='api', title='Valence API options')
|
||||
cfg.CONF.register_group(api_conf_group)
|
||||
cfg.CONF.register_opts(api_opts, group=api_conf_group)
|
||||
cfg.CONF.register_opts(common_opts)
|
||||
logging.register_options(cfg.CONF)
|
||||
|
||||
cfg.CONF(args=args, project='valence',
|
||||
**kwargs)
|
||||
|
||||
rpc.init(cfg.CONF)
|
||||
|
||||
|
||||
def setup_logging():
|
||||
"""Sets up the logging options for a log with supplied name."""
|
||||
product_name = "valence"
|
||||
logging.setup(cfg.CONF, product_name)
|
||||
LOG.info("Logging enabled!")
|
||||
LOG.debug("command line: %s", " ".join(sys.argv))
|
||||
|
||||
|
||||
def list_opts():
|
||||
yield None, common_opts
|
@ -1,44 +0,0 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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_log import log as logging
|
||||
from pecan import expose
|
||||
from pecan import request
|
||||
from valence.controller import api as controller_api
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FlavorController(object):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FlavorController, self).__init__(*args, **kwargs)
|
||||
|
||||
# HTTP GET /flavor/
|
||||
@expose(generic=True, template='json')
|
||||
def index(self):
|
||||
LOG.debug("GET /flavor")
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
res = rpcapi.flavor_options()
|
||||
return res
|
||||
|
||||
# HTTP POST /flavor/
|
||||
@index.when(method='POST', template='json')
|
||||
def index_POST(self, **kw):
|
||||
LOG.debug("POST /flavor")
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
res = rpcapi.flavor_generate(criteria=kw['criteria'])
|
||||
return res
|
@ -1,81 +0,0 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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_log import log as logging
|
||||
import pecan
|
||||
from pecan import expose
|
||||
from pecan import request
|
||||
from pecan.rest import RestController
|
||||
from valence.controller import api as controller_api
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NodeDetailController(RestController):
|
||||
def __init__(self, nodeid):
|
||||
self.nodeid = nodeid
|
||||
|
||||
# HTTP GET /nodes/
|
||||
@expose()
|
||||
def delete(self):
|
||||
LOG.debug("DELETE /nodes")
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
res = rpcapi.delete_composednode(nodeid=self.nodeid)
|
||||
LOG.info(str(res))
|
||||
return res
|
||||
|
||||
@expose()
|
||||
def storages(self):
|
||||
pecan.abort(501, "/nodes/node id/storages")
|
||||
|
||||
|
||||
class NodesController(RestController):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NodesController, self).__init__(*args, **kwargs)
|
||||
|
||||
# HTTP GET /nodes/
|
||||
@expose(template='json')
|
||||
def get_all(self, **kwargs):
|
||||
LOG.debug("GET /nodes")
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
res = rpcapi.list_nodes(filters=kwargs)
|
||||
return res
|
||||
|
||||
# HTTP GET /nodes/
|
||||
@expose(template='json')
|
||||
def post(self, **kwargs):
|
||||
LOG.debug("POST /nodes")
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
res = rpcapi.compose_nodes(criteria=kwargs)
|
||||
return res
|
||||
|
||||
@expose(template='json')
|
||||
def get(self, nodeid):
|
||||
LOG.debug("GET /nodes" + nodeid)
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
node = rpcapi.get_nodebyid(nodeid=nodeid)
|
||||
if not node:
|
||||
pecan.abort(404)
|
||||
return node
|
||||
|
||||
@expose()
|
||||
def _lookup(self, nodeid, *remainder):
|
||||
# node = get_student_by_primary_key(primary_key)
|
||||
if nodeid:
|
||||
return NodeDetailController(nodeid), remainder
|
||||
else:
|
||||
pecan.abort(404)
|
@ -1,44 +0,0 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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_log import log as logging
|
||||
import pecan
|
||||
from pecan import expose
|
||||
from pecan import request
|
||||
from valence.controller import api as controller_api
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StoragesController(object):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(StoragesController, self).__init__(*args, **kwargs)
|
||||
|
||||
# HTTP GET /storages/
|
||||
@expose(generic=True, template='json')
|
||||
def index(self):
|
||||
LOG.debug("GET /storages")
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
LOG.debug(rpcapi)
|
||||
pecan.abort(501, "GET /storages is Not yet implemented")
|
||||
|
||||
@expose(template='json')
|
||||
def get(self, storageid):
|
||||
LOG.debug("GET /storages" + storageid)
|
||||
rpcapi = controller_api.API(context=request.context)
|
||||
LOG.debug(rpcapi)
|
||||
pecan.abort(501, "GET /storages/storage is Not yet implemented")
|
@ -1,14 +0,0 @@
|
||||
from pecan.hooks import PecanHook
|
||||
|
||||
|
||||
class CORSHook(PecanHook):
|
||||
|
||||
def after(self, state):
|
||||
state.response.headers['Access-Control-Allow-Origin'] = '*'
|
||||
state.response.headers['Access-Control-Allow-Methods'] = (
|
||||
'GET, POST, DELETE, PUT, LIST, OPTIONS')
|
||||
state.response.headers['Access-Control-Allow-Headers'] = (
|
||||
'origin, authorization, content-type, accept')
|
||||
if not state.response.headers['Content-Length']:
|
||||
state.response.headers['Content-Length'] = (
|
||||
str(len(state.response.body)))
|
@ -13,19 +13,16 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import pecan
|
||||
from valence.api.controllers import base
|
||||
from valence.api.controllers import types
|
||||
from flask import request
|
||||
from valence.api import base
|
||||
from valence.api import types
|
||||
|
||||
|
||||
def build_url(resource, resource_args, bookmark=False, base_url=None):
|
||||
if base_url is None:
|
||||
base_url = pecan.request.host_url
|
||||
|
||||
base_url = request.root_url
|
||||
base_url = base_url.rstrip("//")
|
||||
template = '%(url)s/%(res)s' if bookmark else '%(url)s/v1/%(res)s'
|
||||
# FIXME(lucasagomes): I'm getting a 404 when doing a GET on
|
||||
# a nested resource that the URL ends with a '/'.
|
||||
# https://groups.google.com/forum/#!topic/pecan-dev/QfSeviLg5qs
|
||||
template += '%(args)s' if resource_args.startswith('?') else '/%(args)s'
|
||||
return template % {'url': base_url, 'res': resource, 'args': resource_args}
|
||||
|
@ -13,13 +13,14 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
from pecan import expose
|
||||
from pecan import request
|
||||
from pecan import route
|
||||
from valence.api.controllers import base
|
||||
from valence.api.controllers import link
|
||||
from valence.api.controllers import types
|
||||
from valence.api.controllers.v1 import controller as v1controller
|
||||
from flask import abort
|
||||
from flask import request
|
||||
from flask_restful import Resource
|
||||
import json
|
||||
from valence.api import base
|
||||
from valence.api import link
|
||||
from valence.api import types
|
||||
from valence.redfish import redfish as rfs
|
||||
|
||||
|
||||
class Version(base.APIBase):
|
||||
@ -32,18 +33,26 @@ class Version(base.APIBase):
|
||||
'links': {
|
||||
'validate': types.List(types.Custom(link.Link)).validate
|
||||
},
|
||||
'min_version': {
|
||||
'validate': types.Text.validate
|
||||
},
|
||||
'status': {
|
||||
'validate': types.Text.validate
|
||||
},
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def convert(id):
|
||||
def convert(id, min_version, current=False):
|
||||
version = Version()
|
||||
version.id = id
|
||||
version.links = [link.Link.make_link('self', request.host_url,
|
||||
version.status = "CURRENT" if current else "DEPRECTED"
|
||||
version.min_version = min_version
|
||||
version.links = [link.Link.make_link('self', request.url_root,
|
||||
id, '', bookmark=True)]
|
||||
return version
|
||||
|
||||
|
||||
class Root(base.APIBase):
|
||||
class RootBase(base.APIBase):
|
||||
|
||||
fields = {
|
||||
'id': {
|
||||
@ -62,17 +71,34 @@ class Root(base.APIBase):
|
||||
|
||||
@staticmethod
|
||||
def convert():
|
||||
root = Root()
|
||||
root = RootBase()
|
||||
root.name = "OpenStack Valence API"
|
||||
root.description = ("Valence is an OpenStack project")
|
||||
root.versions = [Version.convert('v1')]
|
||||
root.default_version = Version.convert('v1')
|
||||
root.description = "Valence is an OpenStack project"
|
||||
root.versions = [Version.convert('v1', '1.0', True)]
|
||||
root.default_version = Version.convert('v1', '1.0', True)
|
||||
return root
|
||||
|
||||
|
||||
class RootController(object):
|
||||
@expose('json')
|
||||
def index(self):
|
||||
return Root.convert()
|
||||
class Root(Resource):
|
||||
|
||||
route(RootController, 'v1', v1controller.V1Controller())
|
||||
def get(self):
|
||||
obj = RootBase.convert()
|
||||
return json.dumps(obj, default=lambda o: o.as_dict())
|
||||
|
||||
|
||||
class PODMProxy(Resource):
|
||||
"""Passthrough Proxy for PODM.
|
||||
|
||||
This function byepasses valence processing
|
||||
and calls PODM directly. This function may be temperory
|
||||
|
||||
"""
|
||||
def get(self, url):
|
||||
op = url.split("/")[0]
|
||||
filterext = ["Chassis", "Services", "Managers", "Systems",
|
||||
"EventService", "Nodes", "EthernetSwitches"]
|
||||
if op in filterext:
|
||||
resp = rfs.send_request(url)
|
||||
return resp.json()
|
||||
else:
|
||||
abort(404)
|
64
valence/api/route.py
Normal file
64
valence/api/route.py
Normal file
@ -0,0 +1,64 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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 flask_cors import CORS
|
||||
from flask_restful import Api
|
||||
from valence.api import app as flaskapp
|
||||
from valence.api.root import PODMProxy
|
||||
from valence.api.root import Root
|
||||
from valence.api.v1.flavor import Flavors as v1Flavors
|
||||
from valence.api.v1.nodes import Nodes as v1Nodes
|
||||
from valence.api.v1.nodes import NodesList as v1NodesList
|
||||
from valence.api.v1.nodes import NodesStorage as v1NodesStorage
|
||||
from valence.api.v1.storages import Storages as v1Storages
|
||||
from valence.api.v1.storages import StoragesList as v1StoragesList
|
||||
from valence.api.v1.systems import Systems as v1Systems
|
||||
from valence.api.v1.systems import SystemsList as v1SystemsList
|
||||
from valence.api.v1.version import V1
|
||||
|
||||
app = flaskapp.get_app()
|
||||
cors = CORS(app)
|
||||
api = Api(app)
|
||||
|
||||
"""API V1.0 Operations"""
|
||||
|
||||
|
||||
# API Root operation
|
||||
api.add_resource(Root, '/', endpoint='root')
|
||||
|
||||
# V1 Root operations
|
||||
api.add_resource(V1, '/v1', endpoint='v1')
|
||||
|
||||
# Node(s) operations
|
||||
api.add_resource(v1NodesList, '/v1/nodes', endpoint='nodes')
|
||||
api.add_resource(v1Nodes, '/v1/nodes/<string:nodeid>', endpoint='node')
|
||||
api.add_resource(v1NodesStorage,
|
||||
'/v1/nodes/<string:nodeid>/storages',
|
||||
endpoint='nodes_storages')
|
||||
|
||||
# System(s) operations
|
||||
api.add_resource(v1SystemsList, '/v1/systems', endpoint='systems')
|
||||
api.add_resource(v1Systems, '/v1/systems/<string:systemid>', endpoint='system')
|
||||
|
||||
# Flavor(s) operations
|
||||
api.add_resource(v1Flavors, '/v1/flavor', endpoint='flavor')
|
||||
|
||||
|
||||
# Storage(s) operations
|
||||
api.add_resource(v1StoragesList, '/v1/storages', endpoint='storages')
|
||||
api.add_resource(v1Storages,
|
||||
'/v1/storages/<string:storageid>', endpoint='storage')
|
||||
|
||||
# Proxy to PODM
|
||||
api.add_resource(PODMProxy, '/<path:url>', endpoint='podmproxy')
|
@ -11,9 +11,7 @@
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
from oslo_utils import strutils
|
||||
import six
|
||||
from valence.common import exceptions as exception
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -27,7 +25,7 @@ class Text(object):
|
||||
return None
|
||||
|
||||
if not isinstance(value, six.string_types):
|
||||
raise exception.InvalidValue(value=value, type=cls.type_name)
|
||||
raise ValueError("An invalid value was provided")
|
||||
|
||||
return value
|
||||
|
||||
@ -41,12 +39,15 @@ class String(object):
|
||||
return None
|
||||
|
||||
try:
|
||||
strutils.check_string_length(value, min_length=min_length,
|
||||
max_length=max_length)
|
||||
strlen = len(value)
|
||||
if strlen < min_length:
|
||||
raise TypeError('String length is less than' + min_length)
|
||||
if max_length and strlen > max_length:
|
||||
raise TypeError('String length is greater than' + max_length)
|
||||
except TypeError:
|
||||
raise exception.InvalidValue(value=value, type=cls.type_name)
|
||||
raise ValueError("An invalid value was provided")
|
||||
except ValueError as e:
|
||||
raise exception.InvalidValue(message=str(e))
|
||||
raise ValueError(str(e))
|
||||
|
||||
return value
|
||||
|
||||
@ -64,12 +65,12 @@ class Integer(object):
|
||||
value = int(value)
|
||||
except Exception:
|
||||
LOG.exception('Failed to convert value to int')
|
||||
raise exception.InvalidValue(value=value, type=cls.type_name)
|
||||
raise ValueError("Failed to convert value to int")
|
||||
|
||||
if minimum is not None and value < minimum:
|
||||
message = _("Integer '%(value)s' is smaller than "
|
||||
"'%(min)d'.") % {'value': value, 'min': minimum}
|
||||
raise exception.InvalidValue(message=message)
|
||||
raise ValueError(message)
|
||||
|
||||
return value
|
||||
|
||||
@ -84,10 +85,10 @@ class Bool(object):
|
||||
|
||||
if not isinstance(value, bool):
|
||||
try:
|
||||
value = strutils.bool_from_string(value, strict=True)
|
||||
value = value.lower() in ("yes", "true", "t", "1")
|
||||
except Exception:
|
||||
LOG.exception('Failed to convert value to bool')
|
||||
raise exception.InvalidValue(value=value, type=cls.type_name)
|
||||
raise ValueError("Failed to convert value to bool")
|
||||
|
||||
return value
|
||||
|
||||
@ -107,7 +108,7 @@ class Custom(object):
|
||||
value = self.user_class(**value)
|
||||
except Exception:
|
||||
LOG.exception('Failed to validate received value')
|
||||
raise exception.InvalidValue(value=value, type=self.type_name)
|
||||
raise ValueError("Failed to validate received value")
|
||||
|
||||
return value
|
||||
|
||||
@ -123,10 +124,10 @@ class List(object):
|
||||
return None
|
||||
|
||||
if not isinstance(value, list):
|
||||
raise exception.InvalidValue(value=value, type=self.type_name)
|
||||
raise ValueError("Failed to validate received value")
|
||||
|
||||
try:
|
||||
return [self.type.validate(v) for v in value]
|
||||
except Exception:
|
||||
LOG.exception('Failed to validate received value')
|
||||
raise exception.InvalidValue(value=value, type=self.type_name)
|
||||
raise ValueError("Failed to validate received value")
|
@ -12,26 +12,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
from flask import request
|
||||
from flask_restful import Resource
|
||||
import logging
|
||||
from valence.flavor import flavor
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Handler(object):
|
||||
"""Valence Flavor RPC handler.
|
||||
class Flavors(Resource):
|
||||
|
||||
These are the backend operations. They are executed by the backend ervice.
|
||||
API calls via AMQP (within the ReST API) trigger the handlers to be called.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(Handler, self).__init__()
|
||||
|
||||
def flavor_options(self, context):
|
||||
def get(self):
|
||||
LOG.debug("GET /flavor")
|
||||
return flavor.get_available_criteria()
|
||||
|
||||
def flavor_generate(self, context, criteria):
|
||||
LOG.debug("Getting flavor options")
|
||||
return flavor.create_flavors(criteria)
|
||||
def post(self):
|
||||
LOG.debug("POST /flavor")
|
||||
return flavor.create_flavors(request.get_json())
|
51
valence/api/v1/nodes.py
Normal file
51
valence/api/v1/nodes.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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 flask import request
|
||||
from flask_restful import abort
|
||||
from flask_restful import Resource
|
||||
import logging
|
||||
from valence.redfish import redfish as rfs
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NodesList(Resource):
|
||||
|
||||
def get(self):
|
||||
LOG.debug("GET /nodes")
|
||||
return rfs.nodes_list(request.args)
|
||||
|
||||
def post(self):
|
||||
LOG.debug("POST /nodes/")
|
||||
return rfs.compose_node(request.get_json())
|
||||
|
||||
|
||||
class Nodes(Resource):
|
||||
|
||||
def get(self, nodeid):
|
||||
LOG.debug("GET /nodes/" + nodeid)
|
||||
return rfs.get_nodebyid(nodeid)
|
||||
|
||||
def delete(self, nodeid):
|
||||
LOG.debug("DELETE /nodes/" + nodeid)
|
||||
return rfs.delete_composednode(nodeid)
|
||||
|
||||
|
||||
class NodesStorage(Resource):
|
||||
|
||||
def get(self, nodeid):
|
||||
LOG.debug("GET /nodes/%s/storage" % nodeid)
|
||||
return abort(501)
|
@ -1,4 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -13,21 +12,22 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from flask_restful import abort
|
||||
from flask_restful import Resource
|
||||
import logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Configurations
|
||||
podm_opts = [
|
||||
cfg.StrOpt('url',
|
||||
default='http://localhost:80',
|
||||
help=("The complete url string of PODM")),
|
||||
cfg.StrOpt('user',
|
||||
default='admin',
|
||||
help=("User for the PODM")),
|
||||
cfg.StrOpt('password',
|
||||
default='admin',
|
||||
help=("Passoword for PODM"))]
|
||||
class StoragesList(Resource):
|
||||
|
||||
podm_conf_group = cfg.OptGroup(name='podm', title='RSC PODM options')
|
||||
cfg.CONF.register_group(podm_conf_group)
|
||||
cfg.CONF.register_opts(podm_opts, group=podm_conf_group)
|
||||
def get(self):
|
||||
LOG.debug("GET /storages")
|
||||
return abort(501)
|
||||
|
||||
|
||||
class Storages(Resource):
|
||||
|
||||
def get(self, storageid):
|
||||
LOG.debug("GET /storages" + storageid)
|
||||
return abort(501)
|
@ -1,3 +1,5 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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
|
||||
@ -10,28 +12,24 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# Server Specific Configurations
|
||||
server = {
|
||||
'port': '8080',
|
||||
'host': '0.0.0.0'
|
||||
}
|
||||
|
||||
# Pecan Application Configurations
|
||||
app = {
|
||||
'root': 'valence.controllers.root.RootController',
|
||||
'modules': ['valence'],
|
||||
'static_root': '%(confdir)s/../../public',
|
||||
'template_path': '%(confdir)s/../templates',
|
||||
'debug': True,
|
||||
'errors': {
|
||||
'404': '/error/404',
|
||||
'__force_dict__': True
|
||||
}
|
||||
}
|
||||
from flask import request
|
||||
from flask_restful import Resource
|
||||
import logging
|
||||
from valence.redfish import redfish as rfs
|
||||
|
||||
# Custom Configurations must be in Python dictionary format::
|
||||
#
|
||||
# foo = {'bar':'baz'}
|
||||
#
|
||||
# All configurations are accessible at::
|
||||
# pecan.conf
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SystemsList(Resource):
|
||||
|
||||
def get(self):
|
||||
LOG.debug("GET /systems")
|
||||
return rfs.systems_list(request.args)
|
||||
|
||||
|
||||
class Systems(Resource):
|
||||
|
||||
def get(self, systemid):
|
||||
LOG.debug("GET /systems/" + systemid)
|
||||
return rfs.get_systembyid(systemid)
|
@ -12,16 +12,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from pecan import abort
|
||||
from pecan import expose
|
||||
from pecan import request
|
||||
from pecan import route
|
||||
from valence.api.controllers import base
|
||||
from valence.api.controllers import link
|
||||
from valence.api.controllers import types
|
||||
from valence.api.controllers.v1 import flavor as v1flavor
|
||||
from valence.api.controllers.v1 import nodes as v1nodes
|
||||
from valence.common.redfish import api as rfsapi
|
||||
|
||||
from flask import request
|
||||
from flask_restful import Resource
|
||||
import json
|
||||
from valence.api import base
|
||||
from valence.api import link
|
||||
from valence.api import types
|
||||
|
||||
|
||||
class MediaType(base.APIBase):
|
||||
@ -37,7 +34,7 @@ class MediaType(base.APIBase):
|
||||
}
|
||||
|
||||
|
||||
class V1(base.APIBase):
|
||||
class V1Base(base.APIBase):
|
||||
"""The representation of the version 1 of the API."""
|
||||
|
||||
fields = {
|
||||
@ -50,16 +47,23 @@ class V1(base.APIBase):
|
||||
'links': {
|
||||
'validate': types.List(types.Custom(link.Link)).validate
|
||||
},
|
||||
'services': {
|
||||
'nodes': {
|
||||
'validate': types.List(types.Custom(link.Link)).validate
|
||||
},
|
||||
'storages': {
|
||||
'validate': types.List(types.Custom(link.Link)).validate
|
||||
},
|
||||
'flavors': {
|
||||
'validate': types.List(types.Custom(link.Link)).validate
|
||||
},
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def convert():
|
||||
v1 = V1()
|
||||
v1 = V1Base()
|
||||
v1.id = "v1"
|
||||
v1.links = [link.Link.make_link('self', request.host_url,
|
||||
v1_base_url = request.url_root.rstrip('//')
|
||||
v1.links = [link.Link.make_link('self', request.url_root,
|
||||
'v1', '', bookmark=True),
|
||||
link.Link.make_link('describedby',
|
||||
'http://docs.openstack.org',
|
||||
@ -68,37 +72,29 @@ class V1(base.APIBase):
|
||||
bookmark=True, type='text/html')]
|
||||
v1.media_types = [MediaType(base='application/json',
|
||||
type='application/vnd.openstack.valence.v1+json')]
|
||||
v1.services = [link.Link.make_link('self', request.host_url,
|
||||
'services', ''),
|
||||
v1.nodes = [link.Link.make_link('self', v1_base_url + '/nodes',
|
||||
'nodes', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
request.host_url,
|
||||
'services', '',
|
||||
v1_base_url + '/nodes',
|
||||
'nodes', '',
|
||||
bookmark=True)]
|
||||
v1.storages = [link.Link.make_link('self', v1_base_url,
|
||||
'storages', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
v1_base_url,
|
||||
'storages', '',
|
||||
bookmark=True)]
|
||||
v1.flavors = [link.Link.make_link('self', v1_base_url,
|
||||
'flavors', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
v1_base_url,
|
||||
'flavors', '',
|
||||
bookmark=True)]
|
||||
return v1
|
||||
|
||||
|
||||
class V1Controller(object):
|
||||
@expose('json')
|
||||
def index(self):
|
||||
return V1.convert()
|
||||
class V1(Resource):
|
||||
|
||||
@expose('json')
|
||||
def _default(self, *args):
|
||||
"""Passthrough Proxy for PODM.
|
||||
|
||||
This function byepasses valence controller handlers
|
||||
and calls PODM directly.
|
||||
|
||||
"""
|
||||
ext = args[0]
|
||||
filterext = ["Chassis", "Services", "Managers", "Systems",
|
||||
"EventService", "Nodes", "EthernetSwitches"]
|
||||
if ext in filterext:
|
||||
urlext = '/'.join(args)
|
||||
resp = rfsapi.send_request(urlext)
|
||||
return resp.json()
|
||||
else:
|
||||
abort(404)
|
||||
|
||||
route(V1Controller, 'flavor', v1flavor.FlavorController())
|
||||
route(V1Controller, 'nodes', v1nodes.NodesController())
|
||||
def get(self):
|
||||
vobj = V1Base.convert()
|
||||
return json.dumps(vobj, default=lambda o: o.as_dict())
|
@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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 sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import wsgi
|
||||
|
||||
from valence.api import app
|
||||
from valence.api import config as api_config
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger('valence.api')
|
||||
|
||||
|
||||
def main():
|
||||
api_config.init(sys.argv[1:])
|
||||
api_config.setup_logging()
|
||||
application = app.setup_app()
|
||||
host = CONF.api.bind_host
|
||||
port = CONF.api.bind_port
|
||||
workers = 1
|
||||
|
||||
LOG.info(("Server on http://%(host)s:%(port)s with %(workers)s"),
|
||||
{'host': host, 'port': port, 'workers': workers})
|
||||
|
||||
service = wsgi.Server(CONF, "valence", application, host, port)
|
||||
|
||||
app.serve(service, CONF, workers)
|
||||
|
||||
LOG.info("Configuration:")
|
||||
app.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Starter script for the Valence controller service."""
|
||||
|
||||
import os
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_service import service
|
||||
import sys
|
||||
import uuid
|
||||
from valence.common import rpc_service
|
||||
from valence.controller import config as controller_config
|
||||
from valence.controller.handlers import flavor_controller
|
||||
from valence.controller.handlers import node_controller
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main():
|
||||
controller_config.init(sys.argv[1:])
|
||||
controller_config.setup_logging()
|
||||
LOG.info(('Starting valence-controller in PID %s'), os.getpid())
|
||||
LOG.debug("Configuration:")
|
||||
controller_id = uuid.uuid4()
|
||||
endpoints = [
|
||||
flavor_controller.Handler(),
|
||||
node_controller.Handler()
|
||||
]
|
||||
|
||||
server = rpc_service.Service.create(cfg.CONF.controller.topic,
|
||||
controller_id, endpoints,
|
||||
binary='valence-controller')
|
||||
launcher = service.launch(cfg.CONF, server)
|
||||
launcher.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,75 +0,0 @@
|
||||
# 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_context import context as oslo_ctx
|
||||
|
||||
|
||||
class ContextBase(oslo_ctx.RequestContext):
|
||||
def __init__(self, auth_token=None, user_id=None, tenant_id=None,
|
||||
is_admin=False, request_id=None, overwrite=True,
|
||||
user_name=None, tenant_name=None, auth_url=None,
|
||||
region=None, password=None, domain='default',
|
||||
project_name=None, **kwargs):
|
||||
super(ContextBase, self).__init__(
|
||||
auth_token=auth_token,
|
||||
user=user_id or kwargs.get('user', None),
|
||||
tenant=tenant_id or kwargs.get('tenant', None),
|
||||
domain=kwargs.get('domain', None),
|
||||
user_domain=kwargs.get('user_domain', None),
|
||||
project_domain=kwargs.get('project_domain', None),
|
||||
is_admin=is_admin,
|
||||
read_only=kwargs.get('read_only', False),
|
||||
show_deleted=kwargs.get('show_deleted', False),
|
||||
request_id=request_id,
|
||||
resource_uuid=kwargs.get('resource_uuid', None),
|
||||
overwrite=overwrite)
|
||||
self.user_name = user_name
|
||||
self.tenant_name = tenant_name
|
||||
self.tenant_id = tenant_id
|
||||
self.auth_url = auth_url
|
||||
self.password = password
|
||||
self.default_name = domain
|
||||
self.region_name = region
|
||||
self.project_name = project_name
|
||||
|
||||
def to_dict(self):
|
||||
ctx_dict = super(ContextBase, self).to_dict()
|
||||
# ctx_dict.update({
|
||||
# to do : dict update
|
||||
# })
|
||||
return ctx_dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, ctx):
|
||||
return cls(**ctx)
|
||||
|
||||
|
||||
class Context(ContextBase):
|
||||
def __init__(self, **kwargs):
|
||||
super(Context, self).__init__(**kwargs)
|
||||
self._session = None
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
return self._session
|
||||
|
||||
|
||||
def get_admin_context(read_only=True):
|
||||
return ContextBase(user_id=None,
|
||||
project_id=None,
|
||||
is_admin=True,
|
||||
overwrite=False,
|
||||
read_only=read_only)
|
||||
|
||||
|
||||
def get_current():
|
||||
return oslo_ctx.get_current()
|
@ -1,79 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
RSC base exception handling.
|
||||
"""
|
||||
import six
|
||||
|
||||
from oslo_utils import excutils
|
||||
|
||||
|
||||
class RSCException(Exception):
|
||||
"""Base RSC Exception."""
|
||||
|
||||
message = "An unknown exception occurred."
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
try:
|
||||
super(RSCException, self).__init__(self.message % kwargs)
|
||||
self.msg = self.message % kwargs
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
if not self.use_fatal_exceptions():
|
||||
ctxt.reraise = False
|
||||
# at least get the core message out if something happened
|
||||
super(RSCException, self).__init__(self.message)
|
||||
|
||||
if six.PY2:
|
||||
def __unicode__(self):
|
||||
return unicode(self.msg)
|
||||
|
||||
def use_fatal_exceptions(self):
|
||||
return False
|
||||
|
||||
|
||||
class BadRequest(RSCException):
|
||||
message = 'Bad %(resource)s request'
|
||||
|
||||
|
||||
class NotImplemented(RSCException):
|
||||
message = ("Not yet implemented in RSC %(func_name)s: ")
|
||||
|
||||
|
||||
class NotFound(RSCException):
|
||||
message = ("URL not Found")
|
||||
|
||||
|
||||
class Conflict(RSCException):
|
||||
pass
|
||||
|
||||
|
||||
class ServiceUnavailable(RSCException):
|
||||
message = "The service is unavailable"
|
||||
|
||||
|
||||
class ConnectionRefused(RSCException):
|
||||
message = "Connection to the service endpoint is refused"
|
||||
|
||||
|
||||
class TimeOut(RSCException):
|
||||
message = "Timeout when connecting to OpenStack Service"
|
||||
|
||||
|
||||
class InternalError(RSCException):
|
||||
message = "Error when performing operation"
|
||||
|
||||
|
||||
class InvalidInputError(RSCException):
|
||||
message = ("An invalid value was provided for %(opt_name)s: "
|
||||
"%(opt_value)s")
|
@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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 json
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
cfg.CONF.import_group('undercloud', 'valence.controller.config')
|
||||
|
||||
|
||||
def _send_request(url, method, headers, requestbody=None):
|
||||
defaultheaders = {'Content-Type': 'application/json'}
|
||||
auth = HTTPBasicAuth(cfg.CONF.undercloud.os_user,
|
||||
cfg.CONF.undercloud.os_password)
|
||||
headers = defaultheaders.update(headers)
|
||||
LOG.debug(url)
|
||||
resp = requests.request(method,
|
||||
url,
|
||||
headers=defaultheaders,
|
||||
data=requestbody,
|
||||
auth=auth)
|
||||
LOG.debug(resp.status_code)
|
||||
return resp.json()
|
||||
|
||||
|
||||
def _get_servicecatalogue_endpoint(keystonejson, servicename):
|
||||
"""Fetch particular endpoint from Keystone.
|
||||
|
||||
This function is to get the particular endpoint from the
|
||||
list of endpoints returned fro keystone.
|
||||
|
||||
"""
|
||||
|
||||
for d in keystonejson["access"]["serviceCatalog"]:
|
||||
if(d["name"] == servicename):
|
||||
return d["endpoints"][0]["publicURL"]
|
||||
|
||||
|
||||
def _get_token_and_url(nameofendpoint):
|
||||
"""Fetch token from the endpoint
|
||||
|
||||
This function get new token and associated endpoint.
|
||||
name of endpoint carries the name of the service whose
|
||||
endpoint need to be found.
|
||||
|
||||
"""
|
||||
|
||||
url = cfg.CONF.undercloud.os_admin_url + "/tokens"
|
||||
data = {"auth":
|
||||
{"tenantName": cfg.CONF.undercloud.os_tenant,
|
||||
"passwordCredentials":
|
||||
{"username": cfg.CONF.undercloud.os_user,
|
||||
"password": cfg.CONF.undercloud.os_password}}}
|
||||
rdata = _send_request(url, "POST", {}, json.dumps(data))
|
||||
tokenid = rdata["access"]["token"]["id"]
|
||||
endpoint = _get_servicecatalogue_endpoint(rdata, nameofendpoint)
|
||||
LOG.debug("Token,Endpoint %s: %s from keystone for %s"
|
||||
% (tokenid, endpoint, nameofendpoint))
|
||||
return (tokenid, endpoint)
|
||||
|
||||
|
||||
# put this function in utils.py later
|
||||
def _get_imageid(jsondata, imgname):
|
||||
# write a generic funciton for this and _get_servicecatalogue_endpoint
|
||||
for d in jsondata["images"]:
|
||||
if(d["name"] == imgname):
|
||||
return d["id"]
|
||||
|
||||
|
||||
def get_undercloud_images():
|
||||
tokenid, endpoint = _get_token_and_url("glance")
|
||||
resp = _send_request(endpoint + "/v2/images",
|
||||
"GET",
|
||||
{'X-Auth-Token': tokenid})
|
||||
imagemap = {"deploy_ramdisk": _get_imageid(resp, "bm-deploy-ramdisk"),
|
||||
"deploy_kernel": _get_imageid(resp, "bm-deploy-kernel"),
|
||||
"image_source": _get_imageid(resp, "overcloud-full"),
|
||||
"ramdisk": _get_imageid(resp, "overcloud-full-initrd"),
|
||||
"kernel": _get_imageid(resp, "overcloud-full-vmlinuz")}
|
||||
return imagemap
|
@ -1,138 +0,0 @@
|
||||
# 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_messaging as messaging
|
||||
# from oslo_serialization import jsonutils
|
||||
# from valence.common import valencecontext
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging as messaging
|
||||
from oslo_serialization import jsonutils
|
||||
from valence.common import context as valence_ctx
|
||||
import valence.common.exceptions
|
||||
|
||||
|
||||
__all__ = [
|
||||
'init',
|
||||
'cleanup',
|
||||
'set_defaults',
|
||||
'add_extra_exmods',
|
||||
'clear_extra_exmods',
|
||||
'get_allowed_exmods',
|
||||
'RequestContextSerializer',
|
||||
'get_client',
|
||||
'get_server',
|
||||
'get_notifier',
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
TRANSPORT = None
|
||||
NOTIFIER = None
|
||||
|
||||
ALLOWED_EXMODS = [
|
||||
valence.common.exceptions.__name__,
|
||||
]
|
||||
EXTRA_EXMODS = []
|
||||
|
||||
|
||||
def init(conf):
|
||||
global TRANSPORT, NOTIFIER
|
||||
exmods = get_allowed_exmods()
|
||||
TRANSPORT = messaging.get_transport(conf,
|
||||
allowed_remote_exmods=exmods)
|
||||
serializer = RequestContextSerializer(JsonPayloadSerializer())
|
||||
NOTIFIER = messaging.Notifier(TRANSPORT, serializer=serializer)
|
||||
|
||||
|
||||
def cleanup():
|
||||
global TRANSPORT, NOTIFIER
|
||||
assert TRANSPORT is not None
|
||||
assert NOTIFIER is not None
|
||||
TRANSPORT.cleanup()
|
||||
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 JsonPayloadSerializer(messaging.NoOpSerializer):
|
||||
@staticmethod
|
||||
def serialize_entity(context, entity):
|
||||
return jsonutils.to_primitive(entity, convert_instances=True)
|
||||
|
||||
|
||||
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):
|
||||
if isinstance(context, dict):
|
||||
return context
|
||||
else:
|
||||
return context.to_dict()
|
||||
|
||||
def deserialize_context(self, context):
|
||||
return valence_ctx.Context.from_dict(context)
|
||||
|
||||
|
||||
def get_transport_url(url_str=None):
|
||||
return messaging.TransportURL.parse(CONF, url_str)
|
||||
|
||||
|
||||
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
|
||||
serializer = RequestContextSerializer(serializer)
|
||||
return messaging.get_rpc_server(TRANSPORT,
|
||||
target,
|
||||
endpoints,
|
||||
executor='eventlet',
|
||||
serializer=serializer)
|
||||
|
||||
|
||||
def get_notifier(service, 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)
|
@ -1,89 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""Common RPC service and API tools for Valence."""
|
||||
|
||||
import eventlet
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging as messaging
|
||||
from oslo_service import service
|
||||
|
||||
from valence.common import rpc
|
||||
from valence.objects import base as objects_base
|
||||
|
||||
eventlet.monkey_patch()
|
||||
|
||||
periodic_opts = [
|
||||
cfg.IntOpt('periodic_interval_max',
|
||||
default=60,
|
||||
help='Max interval size between periodic tasks execution in '
|
||||
'seconds.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(periodic_opts)
|
||||
|
||||
|
||||
class Service(service.Service):
|
||||
|
||||
def __init__(self, topic, server, handlers, binary):
|
||||
super(Service, self).__init__()
|
||||
serializer = rpc.RequestContextSerializer(
|
||||
objects_base.ValenceObjectSerializer())
|
||||
transport = messaging.get_transport(cfg.CONF)
|
||||
# TODO(asalkeld) add support for version='x.y'
|
||||
target = messaging.Target(topic=topic, server=server)
|
||||
self._server = messaging.get_rpc_server(transport, target, handlers,
|
||||
serializer=serializer)
|
||||
self.binary = binary
|
||||
|
||||
def start(self):
|
||||
# servicegroup.setup(CONF, self.binary, self.tg)
|
||||
self._server.start()
|
||||
|
||||
def stop(self):
|
||||
if self._server:
|
||||
self._server.stop()
|
||||
self._server.wait()
|
||||
super(Service, self).stop()
|
||||
|
||||
@classmethod
|
||||
def create(cls, topic, server, handlers, binary):
|
||||
service_obj = cls(topic, server, handlers, binary)
|
||||
return service_obj
|
||||
|
||||
|
||||
class API(object):
|
||||
def __init__(self, transport=None, context=None, topic=None, server=None,
|
||||
timeout=None):
|
||||
serializer = rpc.RequestContextSerializer(
|
||||
objects_base.ValenceObjectSerializer())
|
||||
if transport is None:
|
||||
exmods = rpc.get_allowed_exmods()
|
||||
transport = messaging.get_transport(cfg.CONF,
|
||||
allowed_remote_exmods=exmods)
|
||||
self._context = context
|
||||
if topic is None:
|
||||
topic = ''
|
||||
target = messaging.Target(topic=topic, server=server)
|
||||
self._client = messaging.RPCClient(transport, target,
|
||||
serializer=serializer,
|
||||
timeout=timeout)
|
||||
|
||||
def _call(self, method, *args, **kwargs):
|
||||
return self._client.call(self._context, method, *args, **kwargs)
|
||||
|
||||
def _cast(self, method, *args, **kwargs):
|
||||
self._client.cast(self._context, method, *args, **kwargs)
|
||||
|
||||
def echo(self, message):
|
||||
self._cast('echo', message=message)
|
68
valence/config.py
Normal file
68
valence/config.py
Normal file
@ -0,0 +1,68 @@
|
||||
# Copyright 2016 Intel Corporation
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""This Module reads the configuration from .conf file
|
||||
and set default values if the expected values are not set
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import logging
|
||||
from six.moves import configparser
|
||||
|
||||
|
||||
def get_option(section, key, default, type=str):
|
||||
"""Function to support default values
|
||||
|
||||
Though config fallback feature could be used
|
||||
Py 2.7 doesnt support it
|
||||
|
||||
"""
|
||||
if config.has_option(section, key):
|
||||
return type(config.get(section, key))
|
||||
else:
|
||||
return type(default)
|
||||
|
||||
|
||||
PROJECT_NAME = 'valence'
|
||||
|
||||
config_file = "/etc/%s/%s.conf" % (PROJECT_NAME, PROJECT_NAME)
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_file)
|
||||
|
||||
# Log settings
|
||||
log_level_map = {'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL,
|
||||
'notset': logging.NOTSET}
|
||||
|
||||
log_default_loc = "/var/log/%s/%s.log" % (PROJECT_NAME, PROJECT_NAME)
|
||||
log_default_format = "%(asctime)s %(name)-4s %(levelname)-4s %(message)s"
|
||||
log_level_name = get_option("DEFAULT", "log_level", 'error')
|
||||
|
||||
log_file = get_option("DEFAULT", "log_file", log_default_loc)
|
||||
log_level = log_level_map.get(log_level_name.lower())
|
||||
log_format = get_option("DEFAULT", "log_format", log_default_format)
|
||||
|
||||
# Server Settings
|
||||
bind_port = get_option("DEFAULT", "bind_port", 8181, int)
|
||||
bind_host = get_option("DEFAULT", "bind_host", "0.0.0.0")
|
||||
debug = get_option("DEFAULT", "debug", False, bool)
|
||||
|
||||
# PODM Settings
|
||||
podm_url = get_option("podm", "url", "http://127.0.0.1")
|
||||
podm_user = get_option("podm", "user", "admin")
|
||||
podm_password = get_option("podm", "password", "admin")
|
@ -1,67 +0,0 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""controller API for interfacing with Other modules"""
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from valence.common import rpc_service
|
||||
|
||||
|
||||
# The Backend API class serves as a AMQP client for communicating
|
||||
# on a topic exchange specific to the controllers. This allows the ReST
|
||||
# API to trigger operations on the controllers
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class API(rpc_service.API):
|
||||
def __init__(self, transport=None, context=None, topic=None):
|
||||
if topic is None:
|
||||
cfg.CONF.import_opt('topic', 'valence.controller.config',
|
||||
group='controller')
|
||||
super(API, self).__init__(transport, context,
|
||||
topic=cfg.CONF.controller.topic)
|
||||
|
||||
# Flavor Operations
|
||||
|
||||
def flavor_options(self):
|
||||
return self._call('flavor_options')
|
||||
|
||||
def flavor_generate(self, criteria):
|
||||
return self._call('flavor_generate', criteria=criteria)
|
||||
|
||||
# Node(s) Operations
|
||||
def list_nodes(self, filters):
|
||||
return self._call('list_nodes', filters=filters)
|
||||
|
||||
def get_nodebyid(self, nodeid):
|
||||
return self._call('get_nodebyid', nodeid=nodeid)
|
||||
|
||||
def delete_composednode(self, nodeid):
|
||||
return self._call('delete_composednode', nodeid=nodeid)
|
||||
|
||||
def update_node(self, nodeid):
|
||||
return self._call('update_node')
|
||||
|
||||
def compose_nodes(self, criteria):
|
||||
return self._call('compose_nodes', criteria=criteria)
|
||||
|
||||
def list_node_storages(self, data):
|
||||
return self._call('list_node_storages')
|
||||
|
||||
def map_node_storage(self, data):
|
||||
return self._call('map_node_storage')
|
||||
|
||||
def delete_node_storage(self, data):
|
||||
return self._call('delete_node_storage')
|
@ -1,65 +0,0 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Config options for Valence controller Service"""
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import sys
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONTROLLER_OPTS = [
|
||||
cfg.StrOpt('topic',
|
||||
default='valence-controller',
|
||||
help='The queue to add controller tasks to.')
|
||||
]
|
||||
|
||||
OS_INTERFACE_OPTS = [
|
||||
cfg.StrOpt('os_admin_url',
|
||||
help='Admin URL of Openstack'),
|
||||
cfg.StrOpt('os_tenant',
|
||||
default='admin',
|
||||
help='Tenant for Openstack'),
|
||||
cfg.StrOpt('os_user',
|
||||
default='admin',
|
||||
help='User for openstack'),
|
||||
cfg.StrOpt('os_password',
|
||||
default='addmin',
|
||||
help='Password for openstack')
|
||||
]
|
||||
|
||||
controller_conf_group = cfg.OptGroup(name='controller',
|
||||
title='Valence controller options')
|
||||
cfg.CONF.register_group(controller_conf_group)
|
||||
cfg.CONF.register_opts(CONTROLLER_OPTS, group=controller_conf_group)
|
||||
|
||||
os_conf_group = cfg.OptGroup(name='undercloud',
|
||||
title='Valence Openstack interface options')
|
||||
cfg.CONF.register_group(os_conf_group)
|
||||
cfg.CONF.register_opts(OS_INTERFACE_OPTS, group=os_conf_group)
|
||||
|
||||
|
||||
def init(args, **kwargs):
|
||||
# Register the configuration options
|
||||
logging.register_options(cfg.CONF)
|
||||
cfg.CONF(args=args, project='valence', **kwargs)
|
||||
|
||||
|
||||
def setup_logging():
|
||||
"""Sets up the logging options for a log with supplied name."""
|
||||
domain = "valence"
|
||||
logging.setup(cfg.CONF, domain)
|
||||
LOG.info("Logging enabled!")
|
||||
LOG.debug("command line: %s", " ".join(sys.argv))
|
@ -1,57 +0,0 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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_log import log as logging
|
||||
from valence.common.redfish import api as rfsapi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Handler(object):
|
||||
"""Valence Node RPC handler.
|
||||
|
||||
These are the backend operations. They are executed by the backend ervice.
|
||||
API calls via AMQP (within the ReST API) trigger the handlers to be called.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(Handler, self).__init__()
|
||||
|
||||
def list_nodes(self, context, filters):
|
||||
LOG.info(str(filters))
|
||||
return rfsapi.nodes_list(None, filters)
|
||||
|
||||
def get_nodebyid(self, context, nodeid):
|
||||
return rfsapi.get_nodebyid(nodeid)
|
||||
|
||||
def delete_composednode(self, context, nodeid):
|
||||
return rfsapi.delete_composednode(nodeid)
|
||||
|
||||
def update_node(self, context, nodeid):
|
||||
return {"node": "Update node attributes"}
|
||||
|
||||
def compose_nodes(self, context, criteria):
|
||||
"""Chassis details could also be fetched and inserted"""
|
||||
node_criteria = criteria["filter"] if "filter" in criteria else {}
|
||||
return rfsapi.compose_node(node_criteria)
|
||||
|
||||
def list_node_storages(self, context, data):
|
||||
return {"node": "List the storages attached to the node"}
|
||||
|
||||
def map_node_storage(self, context, data):
|
||||
return {"node": "Map storages to a node"}
|
||||
|
||||
def delete_node_storage(self, context, data):
|
||||
return {"node": "Deleted storages mapped to a node"}
|
@ -13,13 +13,12 @@
|
||||
# under the License.
|
||||
|
||||
from importlib import import_module
|
||||
# from valence.flavor.plugins import *
|
||||
import logging
|
||||
import os
|
||||
from oslo_log import log as logging
|
||||
from valence.common.redfish import api as rfs
|
||||
from valence.redfish import redfish as rfs
|
||||
|
||||
FLAVOR_PLUGIN_PATH = os.path.dirname(os.path.abspath(__file__)) + '/plugins'
|
||||
logger = logging.getLogger()
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_available_criteria():
|
||||
@ -28,27 +27,29 @@ def get_available_criteria():
|
||||
if os.path.isfile(os.path.join(FLAVOR_PLUGIN_PATH, f))
|
||||
and not f.startswith('__') and f.endswith('.py')]
|
||||
resp = []
|
||||
for p in pluginfiles:
|
||||
module = import_module("valence.flavor.plugins." + p)
|
||||
myclass = getattr(module, p + 'Generator')
|
||||
for filename in pluginfiles:
|
||||
module = import_module("valence.flavor.plugins." + filename)
|
||||
myclass = getattr(module, filename + 'Generator')
|
||||
inst = myclass([])
|
||||
resp.append({'name': p, 'description': inst.description()})
|
||||
resp.append({'name': filename, 'description': inst.description()})
|
||||
return {'criteria': resp}
|
||||
|
||||
|
||||
def create_flavors(criteria):
|
||||
def create_flavors(data):
|
||||
"""criteria : comma seperated generator names
|
||||
|
||||
This should be same as thier file name)
|
||||
|
||||
"""
|
||||
criteria = data["criteria"]
|
||||
respjson = []
|
||||
lst_nodes = rfs.nodes_list()
|
||||
for g in criteria.split(","):
|
||||
if g:
|
||||
logger.info("Calling generator : %s ." % g)
|
||||
module = __import__("valence.flavor.plugins." + g, fromlist=["*"])
|
||||
classobj = getattr(module, g + "Generator")
|
||||
inst = classobj(lst_nodes)
|
||||
lst_systems = rfs.systems_list()
|
||||
for criteria_name in criteria.split(","):
|
||||
if criteria_name:
|
||||
LOG.info("Calling generator : %s ." % criteria_name)
|
||||
module = __import__("valence.flavor.plugins." + criteria_name,
|
||||
fromlist=["*"])
|
||||
classobj = getattr(module, criteria_name + "Generator")
|
||||
inst = classobj(lst_systems)
|
||||
respjson.append(inst.generate())
|
||||
return respjson
|
||||
|
@ -12,7 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
import logging
|
||||
import re
|
||||
from valence.flavor.generatorbase import generatorbase
|
||||
|
||||
@ -29,7 +29,7 @@ class assettagGenerator(generatorbase):
|
||||
def generate(self):
|
||||
LOG.info("Default Generator")
|
||||
for node in self.nodes:
|
||||
LOG.info("Node ID " + node['nodeid'])
|
||||
LOG.info("Node ID " + node['id'])
|
||||
location = node['location']
|
||||
location = location.split('Sled')[0]
|
||||
location_lst = re.split("(\d+)", location)
|
||||
|
@ -12,10 +12,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
import logging
|
||||
from valence.flavor.generatorbase import generatorbase
|
||||
|
||||
LOG = logging.getLogger()
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class defaultGenerator(generatorbase):
|
||||
@ -29,14 +29,15 @@ class defaultGenerator(generatorbase):
|
||||
def generate(self):
|
||||
LOG.info("Default Generator")
|
||||
for node in self.nodes:
|
||||
LOG.info("Node ID " + node['nodeid'])
|
||||
LOG.debug("Node ID " + node['id'])
|
||||
location = node['location']
|
||||
LOG.debug(location)
|
||||
location_lst = location.split("_")
|
||||
location_lst = list(filter(None, location_lst))
|
||||
extraspecs = (
|
||||
{l[0]: l[1] for l in (l.split(":") for l in location_lst)})
|
||||
name = self.prepend_name + location
|
||||
return {
|
||||
extraspecs = ({l[0]: l[1]
|
||||
for l in (l.split(":") for l in location_lst)})
|
||||
name = self.prepend_name + node['id']
|
||||
return [
|
||||
self._flavor_template("L_" + name,
|
||||
node['ram'],
|
||||
node['cpu']["count"],
|
||||
@ -52,4 +53,4 @@ class defaultGenerator(generatorbase):
|
||||
int(node['cpu']["count"]) / 4,
|
||||
int(node['storage']) / 4,
|
||||
extraspecs)
|
||||
}
|
||||
]
|
||||
|
@ -12,10 +12,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log as logging
|
||||
import logging
|
||||
from valence.flavor.generatorbase import generatorbase
|
||||
|
||||
logger = logging.getLogger()
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class exampleGenerator(generatorbase):
|
||||
@ -23,5 +23,5 @@ class exampleGenerator(generatorbase):
|
||||
generatorbase.__init__(self, nodes)
|
||||
|
||||
def generate(self):
|
||||
logger.info("Example Flavor Generate")
|
||||
return {"Error": "Example Flavor Generator- Not Yet Implemented"}
|
||||
LOG.info("Example Flavor Generate")
|
||||
return {"Info": "Example Flavor Generator- Not Yet Implemented"}
|
||||
|
@ -1,63 +0,0 @@
|
||||
# Copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Valence common internal object model"""
|
||||
|
||||
from oslo_versionedobjects import base as ovoo_base
|
||||
from oslo_versionedobjects import fields as ovoo_fields
|
||||
|
||||
|
||||
remotable_classmethod = ovoo_base.remotable_classmethod
|
||||
remotable = ovoo_base.remotable
|
||||
|
||||
|
||||
class ValenceObjectRegistry(ovoo_base.VersionedObjectRegistry):
|
||||
pass
|
||||
|
||||
|
||||
class ValenceObject(ovoo_base.VersionedObject):
|
||||
"""Base class and object factory.
|
||||
|
||||
This forms the base of all objects that can be remoted or instantiated
|
||||
via RPC. Simply defining a class that inherits from this base class
|
||||
will make it remotely instantiatable. Objects should implement the
|
||||
necessary "get" classmethod routines as well as "save" object methods
|
||||
as appropriate.
|
||||
"""
|
||||
OBJ_PROJECT_NAMESPACE = 'Valence'
|
||||
|
||||
def as_dict(self):
|
||||
return {k: getattr(self, k)
|
||||
for k in self.fields
|
||||
if self.obj_attr_is_set(k)}
|
||||
|
||||
|
||||
class ValenceObjectDictCompat(ovoo_base.VersionedObjectDictCompat):
|
||||
pass
|
||||
|
||||
|
||||
class ValencePersistentObject(object):
|
||||
"""Mixin class for Persistent objects.
|
||||
|
||||
This adds the fields that we use in common for all persistent objects.
|
||||
"""
|
||||
fields = {
|
||||
'created_at': ovoo_fields.DateTimeField(nullable=True),
|
||||
'updated_at': ovoo_fields.DateTimeField(nullable=True),
|
||||
}
|
||||
|
||||
|
||||
class ValenceObjectSerializer(ovoo_base.VersionedObjectSerializer):
|
||||
# Base class to use for object hydration
|
||||
OBJ_BASE_CLASS = ValenceObject
|
@ -14,14 +14,14 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import logging
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
from valence.common.redfish import tree
|
||||
from valence import config as cfg
|
||||
from valence.redfish import tree
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
cfg.CONF.import_group('podm', 'valence.common.redfish.config')
|
||||
|
||||
|
||||
def get_rfs_url(serviceext):
|
||||
@ -29,17 +29,18 @@ def get_rfs_url(serviceext):
|
||||
INDEX = ''
|
||||
# '/index.json'
|
||||
if REDFISH_BASE_EXT in serviceext:
|
||||
return cfg.CONF.podm.url + serviceext + INDEX
|
||||
return cfg.podm_url + serviceext + INDEX
|
||||
else:
|
||||
return cfg.CONF.podm.url + REDFISH_BASE_EXT + serviceext + INDEX
|
||||
return cfg.podm_url + REDFISH_BASE_EXT + serviceext + INDEX
|
||||
|
||||
|
||||
def send_request(resource, method="GET", **kwargs):
|
||||
# The verify=false param in the request should be removed eventually
|
||||
url = get_rfs_url(resource)
|
||||
httpuser = cfg.CONF.podm.user
|
||||
httppwd = cfg.CONF.podm.password
|
||||
httpuser = cfg.podm_user
|
||||
httppwd = cfg.podm_password
|
||||
resp = None
|
||||
LOG.debug(url)
|
||||
try:
|
||||
resp = requests.request(method, url, verify=False,
|
||||
auth=HTTPBasicAuth(httpuser, httppwd),
|
||||
@ -92,57 +93,6 @@ def generic_filter(jsonContent, filterConditions):
|
||||
return is_filter_passed
|
||||
|
||||
|
||||
def get_details(source):
|
||||
returnJSONObj = []
|
||||
members = source['Members']
|
||||
for member in members:
|
||||
resource = member['@odata.id']
|
||||
resp = send_request(resource)
|
||||
memberJson = resp.json()
|
||||
memberJsonObj = json.loads(memberJson)
|
||||
returnJSONObj[resource] = memberJsonObj
|
||||
return returnJSONObj
|
||||
|
||||
|
||||
def systemdetails():
|
||||
returnJSONObj = []
|
||||
parsed = send_request('Systems')
|
||||
members = parsed['Members']
|
||||
for member in members:
|
||||
resource = member['@odata.id']
|
||||
resp = send_request(resource)
|
||||
memberJsonContent = resp.json()
|
||||
memberJSONObj = json.loads(memberJsonContent)
|
||||
returnJSONObj[resource] = memberJSONObj
|
||||
return(json.dumps(returnJSONObj))
|
||||
|
||||
|
||||
def nodedetails():
|
||||
returnJSONObj = []
|
||||
parsed = send_request('Nodes')
|
||||
members = parsed['Members']
|
||||
for member in members:
|
||||
resource = member['@odata.id']
|
||||
resp = send_request(resource)
|
||||
memberJSONObj = resp.json()
|
||||
returnJSONObj[resource] = memberJSONObj
|
||||
return(json.dumps(returnJSONObj))
|
||||
|
||||
|
||||
def podsdetails():
|
||||
jsonContent = send_request('Chassis')
|
||||
pods = filter_chassis(jsonContent, 'Pod')
|
||||
podsDetails = get_details(pods)
|
||||
return json.dumps(podsDetails)
|
||||
|
||||
|
||||
def racksdetails():
|
||||
jsonContent = send_request('Chassis')
|
||||
racks = filter_chassis(jsonContent, 'Rack')
|
||||
racksDetails = get_details(racks)
|
||||
return json.dumps(racksDetails)
|
||||
|
||||
|
||||
def racks():
|
||||
jsonContent = send_request('Chassis')
|
||||
racks = filter_chassis(jsonContent, 'Rack')
|
||||
@ -165,36 +115,39 @@ def urls2list(url):
|
||||
return []
|
||||
|
||||
|
||||
def extract_val(data, path):
|
||||
def extract_val(data, path, defaultval=None):
|
||||
# function to select value at particularpath
|
||||
patharr = path.split("/")
|
||||
for p in patharr:
|
||||
data = data[p]
|
||||
data = (data if data else defaultval)
|
||||
return data
|
||||
|
||||
|
||||
def node_cpu_details(nodeurl):
|
||||
cpucnt = 0
|
||||
cpuarch = ""
|
||||
cpumodel = ""
|
||||
cpulist = urls2list(nodeurl + '/Processors')
|
||||
for lnk in cpulist:
|
||||
LOG.info("Processing CPU %s" % lnk)
|
||||
resp = send_request(lnk)
|
||||
respdata = resp.json()
|
||||
cpucnt += extract_val(respdata, "TotalCores")
|
||||
cpuarch = extract_val(respdata, "InstructionSet")
|
||||
cpumodel = extract_val(respdata, "Model")
|
||||
# Check if CPU data is populated. It also may have NULL values
|
||||
cpucnt += extract_val(respdata, "TotalCores", 0)
|
||||
cpuarch = extract_val(respdata, "InstructionSet", "")
|
||||
cpumodel = extract_val(respdata, "Model", "")
|
||||
LOG.debug(" Cpu details %s: %d: %s: %s "
|
||||
% (nodeurl, cpucnt, cpuarch, cpumodel))
|
||||
return {"count": str(cpucnt), "arch": cpuarch, "model": cpumodel}
|
||||
return {"cores": str(cpucnt), "arch": cpuarch, "model": cpumodel}
|
||||
|
||||
|
||||
def node_ram_details(nodeurl):
|
||||
# this extracts the RAM and returns as dictionary
|
||||
resp = send_request(nodeurl)
|
||||
respjson = resp.json()
|
||||
ram = extract_val(respjson, "MemorySummary/TotalSystemMemoryGiB")
|
||||
return str(ram) if ram else "0"
|
||||
ram = extract_val(respjson, "MemorySummary/TotalSystemMemoryGiB", "0")
|
||||
return str(ram)
|
||||
|
||||
|
||||
def node_nw_details(nodeurl):
|
||||
@ -214,6 +167,8 @@ def node_storage_details(nodeurl):
|
||||
resp = send_request(lnk)
|
||||
respbody = resp.json()
|
||||
hdds = extract_val(respbody, "Devices")
|
||||
if not hdds:
|
||||
continue
|
||||
for sd in hdds:
|
||||
if "CapacityBytes" in sd:
|
||||
if sd["CapacityBytes"] is not None:
|
||||
@ -223,21 +178,17 @@ def node_storage_details(nodeurl):
|
||||
return str(storagecnt / 1073741824).split(".")[0]
|
||||
|
||||
|
||||
def systems_list(count=None, filters={}):
|
||||
# comment the count value which is set to 2 now..
|
||||
def systems_list(filters={}):
|
||||
# list of nodes with hardware details needed for flavor creation
|
||||
# count = 2
|
||||
lst_systems = []
|
||||
systemurllist = urls2list("Systems")
|
||||
podmtree = build_hierarchy_tree()
|
||||
|
||||
for lnk in systemurllist[:count]:
|
||||
LOG.info(systemurllist)
|
||||
for lnk in systemurllist:
|
||||
filterPassed = True
|
||||
resp = send_request(lnk)
|
||||
system = resp.json()
|
||||
|
||||
# this below code need to be changed when proper query mechanism
|
||||
# is implemented
|
||||
if any(filters):
|
||||
filterPassed = generic_filter(system, filters)
|
||||
if not filterPassed:
|
||||
@ -250,7 +201,7 @@ def systems_list(count=None, filters={}):
|
||||
ram = node_ram_details(lnk)
|
||||
nw = node_nw_details(lnk)
|
||||
storage = node_storage_details(lnk)
|
||||
node = {"nodeid": systemid, "cpu": cpu,
|
||||
system = {"id": systemid, "cpu": cpu,
|
||||
"ram": ram, "storage": storage,
|
||||
"nw": nw, "location": systemlocation,
|
||||
"uuid": systemuuid}
|
||||
@ -274,8 +225,7 @@ def systems_list(count=None, filters={}):
|
||||
else False)
|
||||
|
||||
if filterPassed:
|
||||
lst_systems.append(node)
|
||||
# LOG.info(str(node))
|
||||
lst_systems.append(system)
|
||||
return lst_systems
|
||||
|
||||
|
||||
@ -315,9 +265,12 @@ def get_chassis_list():
|
||||
return lst_chassis
|
||||
|
||||
|
||||
def get_systembyid(systemid):
|
||||
return systems_list({"Id": systemid})
|
||||
|
||||
|
||||
def get_nodebyid(nodeid):
|
||||
resp = send_request("Nodes/" + nodeid)
|
||||
return resp.json()
|
||||
return nodes_list({"Id": nodeid})
|
||||
|
||||
|
||||
def build_hierarchy_tree():
|
||||
@ -338,18 +291,16 @@ def build_hierarchy_tree():
|
||||
return podmtree
|
||||
|
||||
|
||||
def compose_node(criteria={}):
|
||||
def compose_node(data):
|
||||
composeurl = "Nodes/Actions/Allocate"
|
||||
headers = {'Content-type': 'application/json'}
|
||||
criteria = data["criteria"]
|
||||
if not criteria:
|
||||
resp = send_request(composeurl, "POST", headers=headers)
|
||||
else:
|
||||
resp = send_request(composeurl, "POST", json=criteria, headers=headers)
|
||||
LOG.info(resp.headers)
|
||||
LOG.info(resp.text)
|
||||
LOG.info(resp.status_code)
|
||||
composednode = resp.headers['Location']
|
||||
|
||||
composednode = resp.headers['Location']
|
||||
return {"node": composednode}
|
||||
|
||||
|
||||
@ -359,10 +310,9 @@ def delete_composednode(nodeid):
|
||||
return resp
|
||||
|
||||
|
||||
def nodes_list(count=None, filters={}):
|
||||
# comment the count value which is set to 2 now..
|
||||
def nodes_list(filters={}):
|
||||
# list of nodes with hardware details needed for flavor creation
|
||||
# count = 2
|
||||
LOG.debug(filters)
|
||||
lst_nodes = []
|
||||
nodeurllist = urls2list("Nodes")
|
||||
# podmtree = build_hierarchy_tree()
|
||||
@ -376,10 +326,9 @@ def nodes_list(count=None, filters={}):
|
||||
else:
|
||||
node = resp.json()
|
||||
|
||||
# this below code need to be changed when proper query mechanism
|
||||
# is implemented
|
||||
if any(filters):
|
||||
filterPassed = generic_filter(node, filters)
|
||||
LOG.info("FILTER PASSED" + str(filterPassed))
|
||||
if not filterPassed:
|
||||
continue
|
||||
|
||||
@ -392,25 +341,41 @@ def nodes_list(count=None, filters={}):
|
||||
cpu = {}
|
||||
ram = 0
|
||||
nw = 0
|
||||
localstorage = node_storage_details(nodesystemurl)
|
||||
if "Processors" in node:
|
||||
cpu = {"count": node["Processors"]["Count"],
|
||||
"model": node["Processors"]["Model"]}
|
||||
storage = node_storage_details(nodesystemurl)
|
||||
cpu = node_cpu_details(lnk)
|
||||
|
||||
if "Memory" in node:
|
||||
ram = node["Memory"]["TotalSystemMemoryGiB"]
|
||||
|
||||
if "EthernetInterfaces" in node["Links"] and node[
|
||||
"Links"]["EthernetInterfaces"]:
|
||||
if ("EthernetInterfaces" in node["Links"] and
|
||||
node["Links"]["EthernetInterfaces"]):
|
||||
nw = len(node["Links"]["EthernetInterfaces"])
|
||||
|
||||
bmcip = "127.0.0.1" # system['Oem']['Dell_G5MC']['BmcIp']
|
||||
bmcmac = "00:00:00:00:00" # system['Oem']['Dell_G5MC']['BmcMac']
|
||||
node = {"nodeid": nodeid, "cpu": cpu,
|
||||
"ram": ram, "storage": localstorage,
|
||||
node = {"id": nodeid, "cpu": cpu,
|
||||
"ram": ram, "storage": storage,
|
||||
"nw": nw, "location": nodelocation,
|
||||
"uuid": nodeuuid, "bmcip": bmcip, "bmcmac": bmcmac}
|
||||
|
||||
# filter based on RAM, CPU, NETWORK..etc
|
||||
if 'ram' in filters:
|
||||
filterPassed = (True
|
||||
if int(ram) >= int(filters['ram'])
|
||||
else False)
|
||||
|
||||
# filter based on RAM, CPU, NETWORK..etc
|
||||
if 'nw' in filters:
|
||||
filterPassed = (True
|
||||
if int(nw) >= int(filters['nw'])
|
||||
else False)
|
||||
|
||||
# filter based on RAM, CPU, NETWORK..etc
|
||||
if 'storage' in filters:
|
||||
filterPassed = (True
|
||||
if int(storage) >= int(filters['storage'])
|
||||
else False)
|
||||
|
||||
if filterPassed:
|
||||
lst_nodes.append(node)
|
||||
# LOG.info(str(node))
|
||||
return lst_nodes
|
29
valence/run.py
Executable file
29
valence/run.py
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# copyright (c) 2016 Intel, Inc.
|
||||
#
|
||||
# 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 logging
|
||||
from valence.api.route import app as application
|
||||
from valence import config as cfg
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main():
|
||||
application.run(host=cfg.bind_host, port=cfg.bind_port, debug=cfg.debug)
|
||||
LOG.info(("Valence Server on http://%(host)s:%(port)s"),
|
||||
{'host': cfg.bind_host, 'port': cfg.bind_port})
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,7 +1,5 @@
|
||||
import os
|
||||
from pecan import set_config
|
||||
from pecan.testing import load_test_app
|
||||
from unittest import TestCase
|
||||
from valence.api.route import app
|
||||
|
||||
__all__ = ['FunctionalTest']
|
||||
|
||||
@ -15,10 +13,8 @@ class FunctionalTest(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.app = load_test_app(os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'config.py'
|
||||
))
|
||||
self.app = app.test_client()
|
||||
self.app.testing = True
|
||||
|
||||
def tearDown(self):
|
||||
set_config({}, overwrite=True)
|
||||
pass
|
||||
|
@ -1,22 +1,12 @@
|
||||
from valence.tests import FunctionalTest
|
||||
# from unittest import TestCase
|
||||
# from webtest import TestApp
|
||||
|
||||
|
||||
class TestRootController(FunctionalTest):
|
||||
|
||||
def test_get(self):
|
||||
def test_root_get(self):
|
||||
response = self.app.get('/')
|
||||
assert response.status_int == 200
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_search(self):
|
||||
response = self.app.post('/', params={'q': 'RestController'})
|
||||
assert response.status_int == 302
|
||||
assert response.headers['Location'] == (
|
||||
'http://pecan.readthedocs.org/en/latest/search.html'
|
||||
'?q=RestController'
|
||||
)
|
||||
|
||||
def test_get_not_found(self):
|
||||
response = self.app.get('/a/bogus/url', expect_errors=True)
|
||||
assert response.status_int == 404
|
||||
def test_v1_get(self):
|
||||
response = self.app.get('/v1')
|
||||
assert response.status_code == 200
|
||||
|
Loading…
Reference in New Issue
Block a user