Merged trunk
This commit is contained in:
@@ -47,3 +47,7 @@
|
||||
<vishvananda@gmail.com> <root@mirror.nasanebula.net>
|
||||
<vishvananda@gmail.com> <root@ubuntu>
|
||||
<vishvananda@gmail.com> <vishvananda@yahoo.com>
|
||||
<ilyaalekseyev@acm.org> <ialekseev@griddynamics.com>
|
||||
<ilyaalekseyev@acm.org> <ilya@oscloud.ru>
|
||||
<reldan@oscloud.ru> <enugaev@griddynamics.com>
|
||||
<kshileev@gmail.com> <kshileev@griddynamics.com>
|
||||
@@ -22,14 +22,14 @@ David Pravec <David.Pravec@danix.org>
|
||||
Dean Troyer <dtroyer@gmail.com>
|
||||
Devin Carlen <devin.carlen@gmail.com>
|
||||
Ed Leafe <ed@leafe.com>
|
||||
Eldar Nugaev <enugaev@griddynamics.com>
|
||||
Eldar Nugaev <reldan@oscloud.ru>
|
||||
Eric Day <eday@oddments.org>
|
||||
Eric Windisch <eric@cloudscaling.com>
|
||||
Ewan Mellor <ewan.mellor@citrix.com>
|
||||
Gabe Westmaas <gabe.westmaas@rackspace.com>
|
||||
Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp>
|
||||
Hisaki Ohara <hisaki.ohara@intel.com>
|
||||
Ilya Alekseyev <ialekseev@griddynamics.com>
|
||||
Ilya Alekseyev <ilyaalekseyev@acm.org>
|
||||
Isaku Yamahata <yamahata@valinux.co.jp>
|
||||
Jason Cannavale <jason.cannavale@rackspace.com>
|
||||
Jason Koelker <jason@koelker.net>
|
||||
@@ -53,6 +53,7 @@ Kei Masumoto <masumotok@nttdata.co.jp>
|
||||
Ken Pepple <ken.pepple@gmail.com>
|
||||
Kevin Bringard <kbringard@attinteractive.com>
|
||||
Kevin L. Mitchell <kevin.mitchell@rackspace.com>
|
||||
Kirill Shileev <kshileev@gmail.com>
|
||||
Koji Iida <iida.koji@lab.ntt.co.jp>
|
||||
Lorin Hochstein <lorin@isi.edu>
|
||||
Lvov Maxim <usrleon@gmail.com>
|
||||
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2011 Openstack, LLC.
|
||||
# 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.
|
||||
|
||||
"""Cron script to generate usage notifications for instances neither created
|
||||
nor destroyed in a given time period.
|
||||
|
||||
Together with the notifications generated by compute on instance
|
||||
create/delete/resize, over that ime period, this allows an external
|
||||
system consuming usage notification feeds to calculate instance usage
|
||||
for each tenant.
|
||||
|
||||
Time periods are specified like so:
|
||||
<number>[mdy]
|
||||
|
||||
1m = previous month. If the script is run April 1, it will generate usages
|
||||
for March 1 thry March 31.
|
||||
3m = 3 previous months.
|
||||
90d = previous 90 days.
|
||||
1y = previous year. If run on Jan 1, it generates usages for
|
||||
Jan 1 thru Dec 31 of the previous year.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import gettext
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, POSSIBLE_TOPDIR)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import utils
|
||||
|
||||
from nova.notifier import api as notifier_api
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('instance_usage_audit_period', '1m',
|
||||
'time period to generate instance usages for.')
|
||||
|
||||
|
||||
def time_period(period):
|
||||
today = datetime.date.today()
|
||||
unit = period[-1]
|
||||
if unit not in 'mdy':
|
||||
raise ValueError('Time period must be m, d, or y')
|
||||
n = int(period[:-1])
|
||||
if unit == 'm':
|
||||
year = today.year - (n // 12)
|
||||
n = n % 12
|
||||
if n >= today.month:
|
||||
year -= 1
|
||||
month = 12 + (today.month - n)
|
||||
else:
|
||||
month = today.month - n
|
||||
begin = datetime.datetime(day=1, month=month, year=year)
|
||||
end = datetime.datetime(day=1, month=today.month, year=today.year)
|
||||
|
||||
elif unit == 'y':
|
||||
begin = datetime.datetime(day=1, month=1, year=today.year - n)
|
||||
end = datetime.datetime(day=1, month=1, year=today.year)
|
||||
|
||||
elif unit == 'd':
|
||||
b = today - datetime.timedelta(days=n)
|
||||
begin = datetime.datetime(day=b.day, month=b.month, year=b.year)
|
||||
end = datetime.datetime(day=today.day,
|
||||
month=today.month,
|
||||
year=today.year)
|
||||
|
||||
return (begin, end)
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
flags.FLAGS(sys.argv)
|
||||
logging.setup()
|
||||
begin, end = time_period(FLAGS.instance_usage_audit_period)
|
||||
print "Creating usages for %s until %s" % (str(begin), str(end))
|
||||
instances = db.instance_get_active_by_window(context.get_admin_context(),
|
||||
begin,
|
||||
end)
|
||||
print "%s instances" % len(instances)
|
||||
for instance_ref in instances:
|
||||
usage_info = utils.usage_from_instance(instance_ref,
|
||||
audit_period_begining=str(begin),
|
||||
audit_period_ending=str(end))
|
||||
notifier_api.notify('compute.%s' % FLAGS.host,
|
||||
'compute.instance.exists',
|
||||
notifier_api.INFO,
|
||||
usage_info)
|
||||
@@ -137,8 +137,9 @@ if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
FLAGS(sys.argv)
|
||||
logging.setup()
|
||||
server = wsgi.Server()
|
||||
acp_port = FLAGS.ajax_console_proxy_port
|
||||
acp = AjaxConsoleProxy()
|
||||
acp.register_listeners()
|
||||
server.start(acp, FLAGS.ajax_console_proxy_port, host='0.0.0.0')
|
||||
server = wsgi.Server("AJAX Console Proxy", acp, port=acp_port)
|
||||
server.start()
|
||||
server.wait()
|
||||
|
||||
+23
-34
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=C0103
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
@@ -18,44 +17,34 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Starter script for Nova API."""
|
||||
"""Starter script for Nova API.
|
||||
|
||||
Starts both the EC2 and OpenStack APIs in separate processes.
|
||||
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import os
|
||||
import sys
|
||||
|
||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import service
|
||||
from nova import utils
|
||||
from nova import version
|
||||
from nova import wsgi
|
||||
import nova.service
|
||||
import nova.utils
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.api')
|
||||
def main():
|
||||
"""Launch EC2 and OSAPI services."""
|
||||
nova.utils.Bootstrapper.bootstrap_binary(sys.argv)
|
||||
|
||||
ec2 = nova.service.WSGIService("ec2")
|
||||
osapi = nova.service.WSGIService("osapi")
|
||||
|
||||
launcher = nova.service.Launcher()
|
||||
launcher.launch_service(ec2)
|
||||
launcher.launch_service(osapi)
|
||||
|
||||
try:
|
||||
launcher.wait()
|
||||
except KeyboardInterrupt:
|
||||
launcher.stop()
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
FLAGS(sys.argv)
|
||||
logging.setup()
|
||||
LOG.audit(_("Starting nova-api node (version %s)"),
|
||||
version.version_string_with_vcs())
|
||||
LOG.debug(_("Full set of FLAGS:"))
|
||||
for flag in FLAGS:
|
||||
flag_get = FLAGS.get(flag, None)
|
||||
LOG.debug("%(flag)s : %(flag_get)s" % locals())
|
||||
|
||||
service = service.serve_wsgi(service.ApiService)
|
||||
service.wait()
|
||||
sys.exit(main())
|
||||
|
||||
+5
-2
@@ -93,6 +93,9 @@ if __name__ == '__main__':
|
||||
with_req = direct.PostParamsMiddleware(with_json)
|
||||
with_auth = direct.DelegatedAuthMiddleware(with_req)
|
||||
|
||||
server = wsgi.Server()
|
||||
server.start(with_auth, FLAGS.direct_port, host=FLAGS.direct_host)
|
||||
server = wsgi.Server("Direct API",
|
||||
with_auth,
|
||||
host=FLAGS.direct_host,
|
||||
port=FLAGS.direct_port)
|
||||
server.start()
|
||||
server.wait()
|
||||
|
||||
+3
-3
@@ -56,11 +56,11 @@
|
||||
import gettext
|
||||
import glob
|
||||
import json
|
||||
import netaddr
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import IPy
|
||||
|
||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
@@ -518,7 +518,7 @@ class FloatingIpCommands(object):
|
||||
def create(self, host, range):
|
||||
"""Creates floating ips for host by range
|
||||
arguments: host ip_range"""
|
||||
for address in IPy.IP(range):
|
||||
for address in netaddr.IPNetwork(range):
|
||||
db.floating_ip_create(context.get_admin_context(),
|
||||
{'address': str(address),
|
||||
'host': host})
|
||||
@@ -526,7 +526,7 @@ class FloatingIpCommands(object):
|
||||
def delete(self, ip_range):
|
||||
"""Deletes floating ips by range
|
||||
arguments: range"""
|
||||
for address in IPy.IP(ip_range):
|
||||
for address in netaddr.IPNetwork(ip_range):
|
||||
db.floating_ip_destroy(context.get_admin_context(),
|
||||
str(address))
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ if __name__ == '__main__':
|
||||
FLAGS(sys.argv)
|
||||
logging.setup()
|
||||
router = s3server.S3Application(FLAGS.buckets_path)
|
||||
server = wsgi.Server()
|
||||
server.start(router, FLAGS.s3_port, host=FLAGS.s3_host)
|
||||
server = wsgi.Server("S3 Objectstore",
|
||||
router,
|
||||
port=FLAGS.s3_port,
|
||||
host=FLAGS.s3_host)
|
||||
server.start()
|
||||
server.wait()
|
||||
|
||||
+5
-2
@@ -96,6 +96,9 @@ if __name__ == "__main__":
|
||||
|
||||
service.serve()
|
||||
|
||||
server = wsgi.Server()
|
||||
server.start(with_auth, FLAGS.vncproxy_port, host=FLAGS.vncproxy_host)
|
||||
server = wsgi.Server("VNC Proxy",
|
||||
with_auth,
|
||||
host=FLAGS.vncproxy_host,
|
||||
port=FLAGS.vncproxy_port)
|
||||
server.start()
|
||||
server.wait()
|
||||
|
||||
@@ -30,3 +30,8 @@
|
||||
.. moduleauthor:: Manish Singh <yosh@gimp.org>
|
||||
.. moduleauthor:: Andy Smith <andy@anarkystic.com>
|
||||
"""
|
||||
|
||||
import gettext
|
||||
|
||||
|
||||
gettext.install("nova", unicode=1)
|
||||
|
||||
@@ -369,3 +369,23 @@ class AdminController(object):
|
||||
raise exception.ApiError(_('Duplicate rule'))
|
||||
self.compute_api.trigger_provider_fw_rules_refresh(context)
|
||||
return {'status': 'OK', 'message': 'Added %s rules' % rules_added}
|
||||
|
||||
def describe_external_address_blocks(self, context):
|
||||
blocks = db.provider_fw_rule_get_all(context)
|
||||
# NOTE(todd): use a set since we have icmp/udp/tcp rules with same cidr
|
||||
blocks = set([b.cidr for b in blocks])
|
||||
blocks = [{'cidr': b} for b in blocks]
|
||||
return {'externalIpBlockInfo':
|
||||
list(sorted(blocks, key=lambda k: k['cidr']))}
|
||||
|
||||
def remove_external_address_block(self, context, cidr):
|
||||
LOG.audit(_('Removing ip block from %s'), cidr, context=context)
|
||||
cidr = urllib.unquote(cidr).decode()
|
||||
# raise if invalid
|
||||
netaddr.IPNetwork(cidr)
|
||||
rules = db.provider_fw_rule_get_all_by_cidr(context, cidr)
|
||||
for rule in rules:
|
||||
db.provider_fw_rule_destroy(context, rule['id'])
|
||||
if rules:
|
||||
self.compute_api.trigger_provider_fw_rules_refresh(context)
|
||||
return {'status': 'OK', 'message': 'Deleted %s rules' % len(rules)}
|
||||
|
||||
@@ -23,7 +23,7 @@ datastore.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import IPy
|
||||
import netaddr
|
||||
import os
|
||||
import urllib
|
||||
import tempfile
|
||||
@@ -452,7 +452,7 @@ class CloudController(object):
|
||||
elif cidr_ip:
|
||||
# If this fails, it throws an exception. This is what we want.
|
||||
cidr_ip = urllib.unquote(cidr_ip).decode()
|
||||
IPy.IP(cidr_ip)
|
||||
netaddr.IPNetwork(cidr_ip)
|
||||
values['cidr'] = cidr_ip
|
||||
else:
|
||||
values['cidr'] = '0.0.0.0/0'
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 University of Southern California
|
||||
# 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 instance type extra specs extension"""
|
||||
|
||||
from webob import exc
|
||||
|
||||
from nova import db
|
||||
from nova import quota
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import faults
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
|
||||
class FlavorExtraSpecsController(object):
|
||||
""" The flavor extra specs API controller for the Openstack API """
|
||||
|
||||
def _get_extra_specs(self, context, flavor_id):
|
||||
extra_specs = db.api.instance_type_extra_specs_get(context, flavor_id)
|
||||
specs_dict = {}
|
||||
for key, value in extra_specs.iteritems():
|
||||
specs_dict[key] = value
|
||||
return dict(extra_specs=specs_dict)
|
||||
|
||||
def _check_body(self, body):
|
||||
if body == None or body == "":
|
||||
expl = _('No Request Body')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
def index(self, req, flavor_id):
|
||||
""" Returns the list of extra specs for a givenflavor """
|
||||
context = req.environ['nova.context']
|
||||
return self._get_extra_specs(context, flavor_id)
|
||||
|
||||
def create(self, req, flavor_id, body):
|
||||
self._check_body(body)
|
||||
context = req.environ['nova.context']
|
||||
specs = body.get('extra_specs')
|
||||
try:
|
||||
db.api.instance_type_extra_specs_update_or_create(context,
|
||||
flavor_id,
|
||||
specs)
|
||||
except quota.QuotaError as error:
|
||||
self._handle_quota_error(error)
|
||||
return body
|
||||
|
||||
def update(self, req, flavor_id, id, body):
|
||||
self._check_body(body)
|
||||
context = req.environ['nova.context']
|
||||
if not id in body:
|
||||
expl = _('Request body and URI mismatch')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
if len(body) > 1:
|
||||
expl = _('Request body contains too many items')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
try:
|
||||
db.api.instance_type_extra_specs_update_or_create(context,
|
||||
flavor_id,
|
||||
body)
|
||||
except quota.QuotaError as error:
|
||||
self._handle_quota_error(error)
|
||||
|
||||
return body
|
||||
|
||||
def show(self, req, flavor_id, id):
|
||||
""" Return a single extra spec item """
|
||||
context = req.environ['nova.context']
|
||||
specs = self._get_extra_specs(context, flavor_id)
|
||||
if id in specs['extra_specs']:
|
||||
return {id: specs['extra_specs'][id]}
|
||||
else:
|
||||
return faults.Fault(exc.HTTPNotFound())
|
||||
|
||||
def delete(self, req, flavor_id, id):
|
||||
""" Deletes an existing extra spec """
|
||||
context = req.environ['nova.context']
|
||||
db.api.instance_type_extra_specs_delete(context, flavor_id, id)
|
||||
|
||||
def _handle_quota_error(self, error):
|
||||
"""Reraise quota errors as api-specific http exceptions."""
|
||||
if error.code == "MetadataLimitExceeded":
|
||||
raise exc.HTTPBadRequest(explanation=error.message)
|
||||
raise error
|
||||
|
||||
|
||||
class Flavorextraspecs(extensions.ExtensionDescriptor):
|
||||
|
||||
def get_name(self):
|
||||
return "FlavorExtraSpecs"
|
||||
|
||||
def get_alias(self):
|
||||
return "os-flavor-extra-specs"
|
||||
|
||||
def get_description(self):
|
||||
return "Instance type (flavor) extra specs"
|
||||
|
||||
def get_namespace(self):
|
||||
return \
|
||||
"http://docs.openstack.org/ext/flavor_extra_specs/api/v1.1"
|
||||
|
||||
def get_updated(self):
|
||||
return "2011-06-23T00:00:00+00:00"
|
||||
|
||||
def get_resources(self):
|
||||
resources = []
|
||||
res = extensions.ResourceExtension(
|
||||
'os-extra_specs',
|
||||
FlavorExtraSpecsController(),
|
||||
parent=dict(member_name='flavor', collection_name='flavors'))
|
||||
|
||||
resources.append(res)
|
||||
return resources
|
||||
@@ -0,0 +1,172 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# Copyright 2011 Grid Dynamics
|
||||
# Copyright 2011 Eldar Nugaev, Kirill Shileev, Ilya Alekseyev
|
||||
#
|
||||
# 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 webob import exc
|
||||
|
||||
from nova import exception
|
||||
from nova import network
|
||||
from nova import rpc
|
||||
from nova.api.openstack import faults
|
||||
from nova.api.openstack import extensions
|
||||
|
||||
|
||||
def _translate_floating_ip_view(floating_ip):
|
||||
result = {'id': floating_ip['id'],
|
||||
'ip': floating_ip['address']}
|
||||
if 'fixed_ip' in floating_ip:
|
||||
result['fixed_ip'] = floating_ip['fixed_ip']['address']
|
||||
else:
|
||||
result['fixed_ip'] = None
|
||||
if 'instance' in floating_ip:
|
||||
result['instance_id'] = floating_ip['instance']['id']
|
||||
else:
|
||||
result['instance_id'] = None
|
||||
return {'floating_ip': result}
|
||||
|
||||
|
||||
def _translate_floating_ips_view(floating_ips):
|
||||
return {'floating_ips': [_translate_floating_ip_view(floating_ip)
|
||||
for floating_ip in floating_ips]}
|
||||
|
||||
|
||||
class FloatingIPController(object):
|
||||
"""The Floating IPs API controller for the OpenStack API."""
|
||||
|
||||
_serialization_metadata = {
|
||||
'application/xml': {
|
||||
"attributes": {
|
||||
"floating_ip": [
|
||||
"id",
|
||||
"ip",
|
||||
"instance_id",
|
||||
"fixed_ip",
|
||||
]}}}
|
||||
|
||||
def __init__(self):
|
||||
self.network_api = network.API()
|
||||
super(FloatingIPController, self).__init__()
|
||||
|
||||
def show(self, req, id):
|
||||
"""Return data about the given floating ip."""
|
||||
context = req.environ['nova.context']
|
||||
|
||||
try:
|
||||
floating_ip = self.network_api.get_floating_ip(context, id)
|
||||
except exception.NotFound:
|
||||
return faults.Fault(exc.HTTPNotFound())
|
||||
|
||||
return _translate_floating_ip_view(floating_ip)
|
||||
|
||||
def index(self, req):
|
||||
context = req.environ['nova.context']
|
||||
|
||||
floating_ips = self.network_api.list_floating_ips(context)
|
||||
|
||||
return _translate_floating_ips_view(floating_ips)
|
||||
|
||||
def create(self, req, body):
|
||||
context = req.environ['nova.context']
|
||||
|
||||
try:
|
||||
address = self.network_api.allocate_floating_ip(context)
|
||||
ip = self.network_api.get_floating_ip_by_ip(context, address)
|
||||
except rpc.RemoteError as ex:
|
||||
if ex.exc_type == 'NoMoreAddresses':
|
||||
raise exception.NoMoreFloatingIps()
|
||||
else:
|
||||
raise
|
||||
|
||||
return {'allocated': {
|
||||
"id": ip['id'],
|
||||
"floating_ip": ip['address']}}
|
||||
|
||||
def delete(self, req, id):
|
||||
context = req.environ['nova.context']
|
||||
|
||||
ip = self.network_api.get_floating_ip(context, id)
|
||||
self.network_api.release_floating_ip(context, address=ip)
|
||||
|
||||
return {'released': {
|
||||
"id": ip['id'],
|
||||
"floating_ip": ip['address']}}
|
||||
|
||||
def associate(self, req, id, body):
|
||||
""" /floating_ips/{id}/associate fixed ip in body """
|
||||
context = req.environ['nova.context']
|
||||
floating_ip = self._get_ip_by_id(context, id)
|
||||
|
||||
fixed_ip = body['associate_address']['fixed_ip']
|
||||
|
||||
try:
|
||||
self.network_api.associate_floating_ip(context,
|
||||
floating_ip, fixed_ip)
|
||||
except rpc.RemoteError:
|
||||
raise
|
||||
|
||||
return {'associated':
|
||||
{
|
||||
"floating_ip_id": id,
|
||||
"floating_ip": floating_ip,
|
||||
"fixed_ip": fixed_ip}}
|
||||
|
||||
def disassociate(self, req, id, body):
|
||||
""" POST /floating_ips/{id}/disassociate """
|
||||
context = req.environ['nova.context']
|
||||
floating_ip = self.network_api.get_floating_ip(context, id)
|
||||
address = floating_ip['address']
|
||||
fixed_ip = floating_ip['fixed_ip']['address']
|
||||
|
||||
try:
|
||||
self.network_api.disassociate_floating_ip(context, address)
|
||||
except rpc.RemoteError:
|
||||
raise
|
||||
|
||||
return {'disassociated': {'floating_ip': address,
|
||||
'fixed_ip': fixed_ip}}
|
||||
|
||||
def _get_ip_by_id(self, context, value):
|
||||
"""Checks that value is id and then returns its address."""
|
||||
return self.network_api.get_floating_ip(context, value)['address']
|
||||
|
||||
|
||||
class Floating_ips(extensions.ExtensionDescriptor):
|
||||
def get_name(self):
|
||||
return "Floating_ips"
|
||||
|
||||
def get_alias(self):
|
||||
return "os-floating-ips"
|
||||
|
||||
def get_description(self):
|
||||
return "Floating IPs support"
|
||||
|
||||
def get_namespace(self):
|
||||
return "http://docs.openstack.org/ext/floating_ips/api/v1.1"
|
||||
|
||||
def get_updated(self):
|
||||
return "2011-06-16T00:00:00+00:00"
|
||||
|
||||
def get_resources(self):
|
||||
resources = []
|
||||
|
||||
res = extensions.ResourceExtension('os-floating-ips',
|
||||
FloatingIPController(),
|
||||
member_actions={
|
||||
'associate': 'POST',
|
||||
'disassociate': 'POST'})
|
||||
resources.append(res)
|
||||
|
||||
return resources
|
||||
@@ -358,7 +358,7 @@ class Resource(wsgi.Application):
|
||||
def __call__(self, request):
|
||||
"""WSGI method that controls (de)serialization and method dispatch."""
|
||||
|
||||
LOG.debug("%(method)s %(url)s" % {"method": request.method,
|
||||
LOG.info("%(method)s %(url)s" % {"method": request.method,
|
||||
"url": request.url})
|
||||
|
||||
try:
|
||||
@@ -386,7 +386,7 @@ class Resource(wsgi.Application):
|
||||
msg_dict = dict(url=request.url, e=e)
|
||||
msg = _("%(url)s returned a fault: %(e)s" % msg_dict)
|
||||
|
||||
LOG.debug(msg)
|
||||
LOG.info(msg)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@ class OBJECT_CLASS_VIOLATION(Exception): # pylint: disable=C0103
|
||||
pass
|
||||
|
||||
|
||||
class SERVER_DOWN(Exception): # pylint: disable=C0103
|
||||
"""Duplicate exception class from real LDAP module."""
|
||||
pass
|
||||
|
||||
|
||||
def initialize(_uri):
|
||||
"""Opens a fake connection with an LDAP server."""
|
||||
return FakeLDAP()
|
||||
@@ -202,25 +207,38 @@ def _to_json(unencoded):
|
||||
return json.dumps(list(unencoded))
|
||||
|
||||
|
||||
server_fail = False
|
||||
|
||||
|
||||
class FakeLDAP(object):
|
||||
"""Fake LDAP connection."""
|
||||
|
||||
def simple_bind_s(self, dn, password):
|
||||
"""This method is ignored, but provided for compatibility."""
|
||||
if server_fail:
|
||||
raise SERVER_DOWN
|
||||
pass
|
||||
|
||||
def unbind_s(self):
|
||||
"""This method is ignored, but provided for compatibility."""
|
||||
if server_fail:
|
||||
raise SERVER_DOWN
|
||||
pass
|
||||
|
||||
def add_s(self, dn, attr):
|
||||
"""Add an object with the specified attributes at dn."""
|
||||
if server_fail:
|
||||
raise SERVER_DOWN
|
||||
|
||||
key = "%s%s" % (self.__prefix, dn)
|
||||
value_dict = dict([(k, _to_json(v)) for k, v in attr])
|
||||
Store.instance().hmset(key, value_dict)
|
||||
|
||||
def delete_s(self, dn):
|
||||
"""Remove the ldap object at specified dn."""
|
||||
if server_fail:
|
||||
raise SERVER_DOWN
|
||||
|
||||
Store.instance().delete("%s%s" % (self.__prefix, dn))
|
||||
|
||||
def modify_s(self, dn, attrs):
|
||||
@@ -232,6 +250,9 @@ class FakeLDAP(object):
|
||||
([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
|
||||
|
||||
"""
|
||||
if server_fail:
|
||||
raise SERVER_DOWN
|
||||
|
||||
store = Store.instance()
|
||||
key = "%s%s" % (self.__prefix, dn)
|
||||
|
||||
@@ -255,6 +276,9 @@ class FakeLDAP(object):
|
||||
fields -- fields to return. Returns all fields if not specified
|
||||
|
||||
"""
|
||||
if server_fail:
|
||||
raise SERVER_DOWN
|
||||
|
||||
if scope != SCOPE_BASE and scope != SCOPE_SUBTREE:
|
||||
raise NotImplementedError(str(scope))
|
||||
store = Store.instance()
|
||||
|
||||
+37
-2
@@ -101,6 +101,41 @@ def sanitize(fn):
|
||||
return _wrapped
|
||||
|
||||
|
||||
class LDAPWrapper(object):
|
||||
def __init__(self, ldap, url, user, password):
|
||||
self.ldap = ldap
|
||||
self.url = url
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.conn = None
|
||||
|
||||
def __wrap_reconnect(f):
|
||||
def inner(self, *args, **kwargs):
|
||||
if self.conn is None:
|
||||
self.connect()
|
||||
return f(self.conn)(*args, **kwargs)
|
||||
else:
|
||||
try:
|
||||
return f(self.conn)(*args, **kwargs)
|
||||
except self.ldap.SERVER_DOWN:
|
||||
self.connect()
|
||||
return f(self.conn)(*args, **kwargs)
|
||||
return inner
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
self.conn = self.ldap.initialize(self.url)
|
||||
self.conn.simple_bind_s(self.user, self.password)
|
||||
except self.ldap.SERVER_DOWN:
|
||||
self.conn = None
|
||||
raise
|
||||
|
||||
search_s = __wrap_reconnect(lambda conn: conn.search_s)
|
||||
add_s = __wrap_reconnect(lambda conn: conn.add_s)
|
||||
delete_s = __wrap_reconnect(lambda conn: conn.delete_s)
|
||||
modify_s = __wrap_reconnect(lambda conn: conn.modify_s)
|
||||
|
||||
|
||||
class LdapDriver(object):
|
||||
"""Ldap Auth driver
|
||||
|
||||
@@ -124,8 +159,8 @@ class LdapDriver(object):
|
||||
LdapDriver.project_objectclass = 'novaProject'
|
||||
self.__cache = None
|
||||
if LdapDriver.conn is None:
|
||||
LdapDriver.conn = self.ldap.initialize(FLAGS.ldap_url)
|
||||
LdapDriver.conn.simple_bind_s(FLAGS.ldap_user_dn,
|
||||
LdapDriver.conn = LDAPWrapper(self.ldap, FLAGS.ldap_url,
|
||||
FLAGS.ldap_user_dn,
|
||||
FLAGS.ldap_password)
|
||||
if LdapDriver.mc is None:
|
||||
LdapDriver.mc = memcache.Client(FLAGS.memcached_servers, debug=0)
|
||||
|
||||
@@ -50,10 +50,12 @@ import nova.image
|
||||
from nova import log as logging
|
||||
from nova import manager
|
||||
from nova import network
|
||||
from nova import notifier
|
||||
from nova import rpc
|
||||
from nova import utils
|
||||
from nova import volume
|
||||
from nova.compute import power_state
|
||||
from nova.notifier import api as notifier_api
|
||||
from nova.compute.utils import terminate_volumes
|
||||
from nova.virt import driver
|
||||
|
||||
@@ -344,6 +346,11 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
|
||||
self._update_launched_at(context, instance_id)
|
||||
self._update_state(context, instance_id)
|
||||
usage_info = utils.usage_from_instance(instance_ref)
|
||||
notifier_api.notify('compute.%s' % self.host,
|
||||
'compute.instance.create',
|
||||
notifier_api.INFO,
|
||||
usage_info)
|
||||
except exception.InstanceNotFound:
|
||||
# FIXME(wwolf): We are just ignoring InstanceNotFound
|
||||
# exceptions here in case the instance was immediately
|
||||
@@ -422,9 +429,15 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
def terminate_instance(self, context, instance_id):
|
||||
"""Terminate an instance on this host."""
|
||||
self._shutdown_instance(context, instance_id, 'Terminating')
|
||||
instance_ref = self.db.instance_get(context.elevated(), instance_id)
|
||||
|
||||
# TODO(ja): should we keep it in a terminated state for a bit?
|
||||
self.db.instance_destroy(context, instance_id)
|
||||
usage_info = utils.usage_from_instance(instance_ref)
|
||||
notifier_api.notify('compute.%s' % self.host,
|
||||
'compute.instance.delete',
|
||||
notifier_api.INFO,
|
||||
usage_info)
|
||||
|
||||
@exception.wrap_exception
|
||||
@checks_instance_lock
|
||||
@@ -461,6 +474,12 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
self._update_image_ref(context, instance_id, image_ref)
|
||||
self._update_launched_at(context, instance_id)
|
||||
self._update_state(context, instance_id)
|
||||
usage_info = utils.usage_from_instance(instance_ref,
|
||||
image_ref=image_ref)
|
||||
notifier_api.notify('compute.%s' % self.host,
|
||||
'compute.instance.rebuild',
|
||||
notifier_api.INFO,
|
||||
usage_info)
|
||||
|
||||
@exception.wrap_exception
|
||||
@checks_instance_lock
|
||||
@@ -708,6 +727,11 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
context = context.elevated()
|
||||
instance_ref = self.db.instance_get(context, instance_id)
|
||||
self.driver.destroy(instance_ref)
|
||||
usage_info = utils.usage_from_instance(instance_ref)
|
||||
notifier_api.notify('compute.%s' % self.host,
|
||||
'compute.instance.resize.confirm',
|
||||
notifier_api.INFO,
|
||||
usage_info)
|
||||
|
||||
@exception.wrap_exception
|
||||
@checks_instance_lock
|
||||
@@ -755,6 +779,11 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
self.driver.revert_resize(instance_ref)
|
||||
self.db.migration_update(context, migration_id,
|
||||
{'status': 'reverted'})
|
||||
usage_info = utils.usage_from_instance(instance_ref)
|
||||
notifier_api.notify('compute.%s' % self.host,
|
||||
'compute.instance.resize.revert',
|
||||
notifier_api.INFO,
|
||||
usage_info)
|
||||
|
||||
@exception.wrap_exception
|
||||
@checks_instance_lock
|
||||
@@ -791,6 +820,13 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
'migration_id': migration_ref['id'],
|
||||
'instance_id': instance_id, },
|
||||
})
|
||||
usage_info = utils.usage_from_instance(instance_ref,
|
||||
new_instance_type=instance_type['name'],
|
||||
new_instance_type_id=instance_type['id'])
|
||||
notifier_api.notify('compute.%s' % self.host,
|
||||
'compute.instance.resize.prep',
|
||||
notifier_api.INFO,
|
||||
usage_info)
|
||||
|
||||
@exception.wrap_exception
|
||||
@checks_instance_lock
|
||||
|
||||
@@ -223,6 +223,9 @@ def certificate_update(context, certificate_id, values):
|
||||
|
||||
###################
|
||||
|
||||
def floating_ip_get(context, floating_ip_id):
|
||||
return IMPL.floating_ip_get(context, floating_ip_id)
|
||||
|
||||
|
||||
def floating_ip_allocate_address(context, host, project_id):
|
||||
"""Allocate free floating ip and return the address.
|
||||
@@ -289,6 +292,11 @@ def floating_ip_get_by_address(context, address):
|
||||
return IMPL.floating_ip_get_by_address(context, address)
|
||||
|
||||
|
||||
def floating_ip_get_by_ip(context, ip):
|
||||
"""Get a floating ip by floating address."""
|
||||
return IMPL.floating_ip_get_by_ip(context, ip)
|
||||
|
||||
|
||||
def floating_ip_update(context, address, values):
|
||||
"""Update a floating ip by address or raise if it doesn't exist."""
|
||||
return IMPL.floating_ip_update(context, address, values)
|
||||
@@ -434,6 +442,11 @@ def instance_get_all(context):
|
||||
return IMPL.instance_get_all(context)
|
||||
|
||||
|
||||
def instance_get_active_by_window(context, begin, end=None):
|
||||
"""Get instances active during a certain time window."""
|
||||
return IMPL.instance_get_active_by_window(context, begin, end)
|
||||
|
||||
|
||||
def instance_get_all_by_user(context, user_id):
|
||||
"""Get all instances."""
|
||||
return IMPL.instance_get_all_by_user(context, user_id)
|
||||
@@ -1044,6 +1057,16 @@ def provider_fw_rule_get_all(context):
|
||||
return IMPL.provider_fw_rule_get_all(context)
|
||||
|
||||
|
||||
def provider_fw_rule_get_all_by_cidr(context, cidr):
|
||||
"""Get all provider-level firewall rules."""
|
||||
return IMPL.provider_fw_rule_get_all_by_cidr(context, cidr)
|
||||
|
||||
|
||||
def provider_fw_rule_destroy(context, rule_id):
|
||||
"""Delete a provider firewall rule from the database."""
|
||||
return IMPL.provider_fw_rule_destroy(context, rule_id)
|
||||
|
||||
|
||||
###################
|
||||
|
||||
|
||||
@@ -1329,3 +1352,24 @@ def agent_build_destroy(context, agent_update_id):
|
||||
def agent_build_update(context, agent_build_id, values):
|
||||
"""Update agent build entry."""
|
||||
IMPL.agent_build_update(context, agent_build_id, values)
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
def instance_type_extra_specs_get(context, instance_type_id):
|
||||
"""Get all extra specs for an instance type."""
|
||||
return IMPL.instance_type_extra_specs_get(context, instance_type_id)
|
||||
|
||||
|
||||
def instance_type_extra_specs_delete(context, instance_type_id, key):
|
||||
"""Delete the given extra specs item."""
|
||||
IMPL.instance_type_extra_specs_delete(context, instance_type_id, key)
|
||||
|
||||
|
||||
def instance_type_extra_specs_update_or_create(context, instance_type_id,
|
||||
extra_specs):
|
||||
"""Create or update instance type extra specs. This adds or modifies the
|
||||
key/value pairs specified in the extra specs dict argument"""
|
||||
IMPL.instance_type_extra_specs_update_or_create(context, instance_type_id,
|
||||
extra_specs)
|
||||
|
||||
+194
-6
@@ -428,6 +428,29 @@ def certificate_update(context, certificate_id, values):
|
||||
|
||||
|
||||
###################
|
||||
@require_context
|
||||
def floating_ip_get(context, id):
|
||||
session = get_session()
|
||||
result = None
|
||||
if is_admin_context(context):
|
||||
result = session.query(models.FloatingIp).\
|
||||
options(joinedload('fixed_ip')).\
|
||||
options(joinedload_all('fixed_ip.instance')).\
|
||||
filter_by(id=id).\
|
||||
filter_by(deleted=can_read_deleted(context)).\
|
||||
first()
|
||||
elif is_user_context(context):
|
||||
result = session.query(models.FloatingIp).\
|
||||
options(joinedload('fixed_ip')).\
|
||||
options(joinedload_all('fixed_ip.instance')).\
|
||||
filter_by(project_id=context.project_id).\
|
||||
filter_by(id=id).\
|
||||
filter_by(deleted=False).\
|
||||
first()
|
||||
if not result:
|
||||
raise exception.FloatingIpNotFoundForFixedAddress()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@require_context
|
||||
@@ -582,7 +605,23 @@ def floating_ip_get_by_address(context, address, session=None):
|
||||
filter_by(deleted=can_read_deleted(context)).\
|
||||
first()
|
||||
if not result:
|
||||
raise exception.FloatingIpNotFound(fixed_ip=address)
|
||||
raise exception.FloatingIpNotFoundForFixedAddress(fixed_ip=address)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@require_context
|
||||
def floating_ip_get_by_ip(context, ip, session=None):
|
||||
if not session:
|
||||
session = get_session()
|
||||
|
||||
result = session.query(models.FloatingIp).\
|
||||
filter_by(address=ip).\
|
||||
filter_by(deleted=can_read_deleted(context)).\
|
||||
first()
|
||||
|
||||
if not result:
|
||||
raise exception.FloatingIpNotFound(floating_ip=ip)
|
||||
|
||||
return result
|
||||
|
||||
@@ -722,7 +761,7 @@ def fixed_ip_get_by_address(context, address, session=None):
|
||||
options(joinedload('instance')).\
|
||||
first()
|
||||
if not result:
|
||||
raise exception.FloatingIpNotFound(fixed_ip=address)
|
||||
raise exception.FloatingIpNotFoundForFixedAddress(fixed_ip=address)
|
||||
|
||||
if is_user_context(context):
|
||||
authorize_project_context(context, result.instance.project_id)
|
||||
@@ -916,6 +955,24 @@ def instance_get_all(context):
|
||||
all()
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def instance_get_active_by_window(context, begin, end=None):
|
||||
"""Return instances that were continuously active over the given window"""
|
||||
session = get_session()
|
||||
query = session.query(models.Instance).\
|
||||
options(joinedload_all('fixed_ip.floating_ips')).\
|
||||
options(joinedload('security_groups')).\
|
||||
options(joinedload_all('fixed_ip.network')).\
|
||||
options(joinedload('instance_type')).\
|
||||
filter(models.Instance.launched_at < begin)
|
||||
if end:
|
||||
query = query.filter(or_(models.Instance.terminated_at == None,
|
||||
models.Instance.terminated_at > end))
|
||||
else:
|
||||
query = query.filter(models.Instance.terminated_at == None)
|
||||
return query.all()
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def instance_get_all_by_user(context, user_id):
|
||||
session = get_session()
|
||||
@@ -2189,6 +2246,7 @@ def provider_fw_rule_create(context, rule):
|
||||
return fw_rule_ref
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def provider_fw_rule_get_all(context):
|
||||
session = get_session()
|
||||
return session.query(models.ProviderFirewallRule).\
|
||||
@@ -2196,6 +2254,26 @@ def provider_fw_rule_get_all(context):
|
||||
all()
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def provider_fw_rule_get_all_by_cidr(context, cidr):
|
||||
session = get_session()
|
||||
return session.query(models.ProviderFirewallRule).\
|
||||
filter_by(deleted=can_read_deleted(context)).\
|
||||
filter_by(cidr=cidr).\
|
||||
all()
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def provider_fw_rule_destroy(context, rule_id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
session.query(models.ProviderFirewallRule).\
|
||||
filter_by(id=rule_id).\
|
||||
update({'deleted': True,
|
||||
'deleted_at': utils.utcnow(),
|
||||
'updated_at': literal_column('updated_at')})
|
||||
|
||||
|
||||
###################
|
||||
|
||||
|
||||
@@ -2576,7 +2654,22 @@ def console_get(context, console_id, instance_id=None):
|
||||
|
||||
@require_admin_context
|
||||
def instance_type_create(_context, values):
|
||||
"""Create a new instance type. In order to pass in extra specs,
|
||||
the values dict should contain a 'extra_specs' key/value pair:
|
||||
|
||||
{'extra_specs' : {'k1': 'v1', 'k2': 'v2', ...}}
|
||||
|
||||
"""
|
||||
try:
|
||||
specs = values.get('extra_specs')
|
||||
specs_refs = []
|
||||
if specs:
|
||||
for k, v in specs.iteritems():
|
||||
specs_ref = models.InstanceTypeExtraSpecs()
|
||||
specs_ref['key'] = k
|
||||
specs_ref['value'] = v
|
||||
specs_refs.append(specs_ref)
|
||||
values['extra_specs'] = specs_refs
|
||||
instance_type_ref = models.InstanceTypes()
|
||||
instance_type_ref.update(values)
|
||||
instance_type_ref.save()
|
||||
@@ -2585,6 +2678,25 @@ def instance_type_create(_context, values):
|
||||
return instance_type_ref
|
||||
|
||||
|
||||
def _dict_with_extra_specs(inst_type_query):
|
||||
"""Takes an instance type query returned by sqlalchemy
|
||||
and returns it as a dictionary, converting the extra_specs
|
||||
entry from a list of dicts:
|
||||
|
||||
'extra_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...]
|
||||
|
||||
to a single dict:
|
||||
|
||||
'extra_specs' : {'k1': 'v1'}
|
||||
|
||||
"""
|
||||
inst_type_dict = dict(inst_type_query)
|
||||
extra_specs = dict([(x['key'], x['value']) for x in \
|
||||
inst_type_query['extra_specs']])
|
||||
inst_type_dict['extra_specs'] = extra_specs
|
||||
return inst_type_dict
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_type_get_all(context, inactive=False):
|
||||
"""
|
||||
@@ -2593,17 +2705,19 @@ def instance_type_get_all(context, inactive=False):
|
||||
session = get_session()
|
||||
if inactive:
|
||||
inst_types = session.query(models.InstanceTypes).\
|
||||
options(joinedload('extra_specs')).\
|
||||
order_by("name").\
|
||||
all()
|
||||
else:
|
||||
inst_types = session.query(models.InstanceTypes).\
|
||||
options(joinedload('extra_specs')).\
|
||||
filter_by(deleted=False).\
|
||||
order_by("name").\
|
||||
all()
|
||||
if inst_types:
|
||||
inst_dict = {}
|
||||
for i in inst_types:
|
||||
inst_dict[i['name']] = dict(i)
|
||||
inst_dict[i['name']] = _dict_with_extra_specs(i)
|
||||
return inst_dict
|
||||
else:
|
||||
raise exception.NoInstanceTypesFound()
|
||||
@@ -2614,12 +2728,14 @@ def instance_type_get_by_id(context, id):
|
||||
"""Returns a dict describing specific instance_type"""
|
||||
session = get_session()
|
||||
inst_type = session.query(models.InstanceTypes).\
|
||||
options(joinedload('extra_specs')).\
|
||||
filter_by(id=id).\
|
||||
first()
|
||||
|
||||
if not inst_type:
|
||||
raise exception.InstanceTypeNotFound(instance_type=id)
|
||||
else:
|
||||
return dict(inst_type)
|
||||
return _dict_with_extra_specs(inst_type)
|
||||
|
||||
|
||||
@require_context
|
||||
@@ -2627,12 +2743,13 @@ def instance_type_get_by_name(context, name):
|
||||
"""Returns a dict describing specific instance_type"""
|
||||
session = get_session()
|
||||
inst_type = session.query(models.InstanceTypes).\
|
||||
options(joinedload('extra_specs')).\
|
||||
filter_by(name=name).\
|
||||
first()
|
||||
if not inst_type:
|
||||
raise exception.InstanceTypeNotFoundByName(instance_type_name=name)
|
||||
else:
|
||||
return dict(inst_type)
|
||||
return _dict_with_extra_specs(inst_type)
|
||||
|
||||
|
||||
@require_context
|
||||
@@ -2640,12 +2757,13 @@ def instance_type_get_by_flavor_id(context, id):
|
||||
"""Returns a dict describing specific flavor_id"""
|
||||
session = get_session()
|
||||
inst_type = session.query(models.InstanceTypes).\
|
||||
options(joinedload('extra_specs')).\
|
||||
filter_by(flavorid=int(id)).\
|
||||
first()
|
||||
if not inst_type:
|
||||
raise exception.FlavorNotFound(flavor_id=id)
|
||||
else:
|
||||
return dict(inst_type)
|
||||
return _dict_with_extra_specs(inst_type)
|
||||
|
||||
|
||||
@require_admin_context
|
||||
@@ -2813,6 +2931,9 @@ def instance_metadata_update_or_create(context, instance_id, metadata):
|
||||
return metadata
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def agent_build_create(context, values):
|
||||
agent_build_ref = models.AgentBuild()
|
||||
@@ -2862,3 +2983,70 @@ def agent_build_update(context, agent_build_id, values):
|
||||
first()
|
||||
agent_build_ref.update(values)
|
||||
agent_build_ref.save(session=session)
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_type_extra_specs_get(context, instance_type_id):
|
||||
session = get_session()
|
||||
|
||||
spec_results = session.query(models.InstanceTypeExtraSpecs).\
|
||||
filter_by(instance_type_id=instance_type_id).\
|
||||
filter_by(deleted=False).\
|
||||
all()
|
||||
|
||||
spec_dict = {}
|
||||
for i in spec_results:
|
||||
spec_dict[i['key']] = i['value']
|
||||
return spec_dict
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_type_extra_specs_delete(context, instance_type_id, key):
|
||||
session = get_session()
|
||||
session.query(models.InstanceTypeExtraSpecs).\
|
||||
filter_by(instance_type_id=instance_type_id).\
|
||||
filter_by(key=key).\
|
||||
filter_by(deleted=False).\
|
||||
update({'deleted': True,
|
||||
'deleted_at': utils.utcnow(),
|
||||
'updated_at': literal_column('updated_at')})
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_type_extra_specs_get_item(context, instance_type_id, key):
|
||||
session = get_session()
|
||||
|
||||
sppec_result = session.query(models.InstanceTypeExtraSpecs).\
|
||||
filter_by(instance_type_id=instance_type_id).\
|
||||
filter_by(key=key).\
|
||||
filter_by(deleted=False).\
|
||||
first()
|
||||
|
||||
if not spec_result:
|
||||
raise exception.\
|
||||
InstanceTypeExtraSpecsNotFound(extra_specs_key=key,
|
||||
instance_type_id=instance_type_id)
|
||||
return spec_result
|
||||
|
||||
|
||||
@require_context
|
||||
def instance_type_extra_specs_update_or_create(context, instance_type_id,
|
||||
specs):
|
||||
session = get_session()
|
||||
spec_ref = None
|
||||
for key, value in specs.iteritems():
|
||||
try:
|
||||
spec_ref = instance_type_extra_specs_get_item(context,
|
||||
instance_type_id,
|
||||
key,
|
||||
session)
|
||||
except:
|
||||
spec_ref = models.InstanceTypeExtraSpecs()
|
||||
spec_ref.update({"key": key, "value": value,
|
||||
"instance_type_id": instance_type_id,
|
||||
"deleted": 0})
|
||||
spec_ref.save(session=session)
|
||||
return specs
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 University of Southern California
|
||||
#
|
||||
# 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 sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer
|
||||
from sqlalchemy import MetaData, String, Table
|
||||
from nova import log as logging
|
||||
|
||||
meta = MetaData()
|
||||
|
||||
# Just for the ForeignKey and column creation to succeed, these are not the
|
||||
# actual definitions of instances or services.
|
||||
instance_types = Table('instance_types', meta,
|
||||
Column('id', Integer(), primary_key=True, nullable=False),
|
||||
)
|
||||
|
||||
#
|
||||
# New Tables
|
||||
#
|
||||
|
||||
instance_type_extra_specs_table = Table('instance_type_extra_specs', meta,
|
||||
Column('created_at', DateTime(timezone=False)),
|
||||
Column('updated_at', DateTime(timezone=False)),
|
||||
Column('deleted_at', DateTime(timezone=False)),
|
||||
Column('deleted', Boolean(create_constraint=True, name=None)),
|
||||
Column('id', Integer(), primary_key=True, nullable=False),
|
||||
Column('instance_type_id',
|
||||
Integer(),
|
||||
ForeignKey('instance_types.id'),
|
||||
nullable=False),
|
||||
Column('key',
|
||||
String(length=255, convert_unicode=False, assert_unicode=None,
|
||||
unicode_error=None, _warn_on_bytestring=False)),
|
||||
Column('value',
|
||||
String(length=255, convert_unicode=False, assert_unicode=None,
|
||||
unicode_error=None, _warn_on_bytestring=False)))
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
# Upgrade operations go here. Don't create your own engine;
|
||||
# bind migrate_engine to your metadata
|
||||
meta.bind = migrate_engine
|
||||
for table in (instance_type_extra_specs_table, ):
|
||||
try:
|
||||
table.create()
|
||||
except Exception:
|
||||
logging.info(repr(table))
|
||||
logging.exception('Exception while creating table')
|
||||
raise
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
# Operations to reverse the above upgrade go here.
|
||||
for table in (instance_type_extra_specs_table, ):
|
||||
table.drop()
|
||||
@@ -716,6 +716,21 @@ class InstanceMetadata(BASE, NovaBase):
|
||||
'InstanceMetadata.deleted == False)')
|
||||
|
||||
|
||||
class InstanceTypeExtraSpecs(BASE, NovaBase):
|
||||
"""Represents additional specs as key/value pairs for an instance_type"""
|
||||
__tablename__ = 'instance_type_extra_specs'
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column(String(255))
|
||||
value = Column(String(255))
|
||||
instance_type_id = Column(Integer, ForeignKey('instance_types.id'),
|
||||
nullable=False)
|
||||
instance_type = relationship(InstanceTypes, backref="extra_specs",
|
||||
foreign_keys=instance_type_id,
|
||||
primaryjoin='and_('
|
||||
'InstanceTypeExtraSpecs.instance_type_id == InstanceTypes.id,'
|
||||
'InstanceTypeExtraSpecs.deleted == False)')
|
||||
|
||||
|
||||
class Zone(BASE, NovaBase):
|
||||
"""Represents a child zone of this zone."""
|
||||
__tablename__ = 'zones'
|
||||
@@ -750,7 +765,7 @@ def register_models():
|
||||
Network, SecurityGroup, SecurityGroupIngressRule,
|
||||
SecurityGroupInstanceAssociation, AuthToken, User,
|
||||
Project, Certificate, ConsolePool, Console, Zone,
|
||||
AgentBuild, InstanceMetadata, Migration)
|
||||
AgentBuild, InstanceMetadata, InstanceTypeExtraSpecs, Migration)
|
||||
engine = create_engine(FLAGS.sql_connection, echo=False)
|
||||
for model in models:
|
||||
model.metadata.create_all(engine)
|
||||
|
||||
@@ -361,6 +361,10 @@ class NoFixedIpsFoundForInstance(NotFound):
|
||||
|
||||
|
||||
class FloatingIpNotFound(NotFound):
|
||||
message = _("Floating ip %(floating_ip)s not found")
|
||||
|
||||
|
||||
class FloatingIpNotFoundForFixedAddress(NotFound):
|
||||
message = _("Floating ip not found for fixed address %(fixed_ip)s.")
|
||||
|
||||
|
||||
@@ -504,6 +508,11 @@ class InstanceMetadataNotFound(NotFound):
|
||||
"key %(metadata_key)s.")
|
||||
|
||||
|
||||
class InstanceTypeExtraSpecsNotFound(NotFound):
|
||||
message = _("Instance Type %(instance_type_id)s has no extra specs with "
|
||||
"key %(extra_specs_key)s.")
|
||||
|
||||
|
||||
class LDAPObjectNotFound(NotFound):
|
||||
message = _("LDAP object could not be found")
|
||||
|
||||
@@ -597,3 +606,11 @@ class MigrationError(NovaException):
|
||||
|
||||
class MalformedRequestBody(NovaException):
|
||||
message = _("Malformed message body: %(reason)s")
|
||||
|
||||
|
||||
class PasteConfigNotFound(NotFound):
|
||||
message = _("Could not find paste config at %(path)s")
|
||||
|
||||
|
||||
class PasteAppNotFound(NotFound):
|
||||
message = _("Could not load paste app '%(name)s' from %(path)s")
|
||||
|
||||
@@ -59,7 +59,7 @@ class GlanceImageService(service.BaseImageService):
|
||||
"""Provides storage and retrieval of disk image objects within Glance."""
|
||||
|
||||
GLANCE_ONLY_ATTRS = ['size', 'location', 'disk_format',
|
||||
'container_format']
|
||||
'container_format', 'checksum']
|
||||
|
||||
# NOTE(sirp): Overriding to use _translate_to_service provided by
|
||||
# BaseImageService
|
||||
|
||||
+11
@@ -314,3 +314,14 @@ logging.setLoggerClass(NovaLogger)
|
||||
def audit(msg, *args, **kwargs):
|
||||
"""Shortcut for logging to root log with sevrity 'AUDIT'."""
|
||||
logging.root.log(AUDIT, msg, *args, **kwargs)
|
||||
|
||||
|
||||
class WritableLogger(object):
|
||||
"""A thin wrapper that responds to `write` and logs."""
|
||||
|
||||
def __init__(self, logger, level=logging.INFO):
|
||||
self.logger = logger
|
||||
self.level = level
|
||||
|
||||
def write(self, msg):
|
||||
self.logger.log(self.level, msg)
|
||||
|
||||
@@ -34,6 +34,19 @@ LOG = logging.getLogger('nova.network')
|
||||
class API(base.Base):
|
||||
"""API for interacting with the network manager."""
|
||||
|
||||
def get_floating_ip(self, context, id):
|
||||
rv = self.db.floating_ip_get(context, id)
|
||||
return dict(rv.iteritems())
|
||||
|
||||
def get_floating_ip_by_ip(self, context, address):
|
||||
res = self.db.floating_ip_get_by_ip(context, address)
|
||||
return dict(res.iteritems())
|
||||
|
||||
def list_floating_ips(self, context):
|
||||
ips = self.db.floating_ip_get_all_by_project(context,
|
||||
context.project_id)
|
||||
return ips
|
||||
|
||||
def allocate_floating_ip(self, context):
|
||||
if quota.allowed_floating_ips(context, 1) < 1:
|
||||
LOG.warn(_('Quota exceeeded for %s, tried to allocate '
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
import calendar
|
||||
import inspect
|
||||
import netaddr
|
||||
import os
|
||||
|
||||
from nova import db
|
||||
@@ -27,7 +28,6 @@ from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import utils
|
||||
from IPy import IP
|
||||
|
||||
|
||||
LOG = logging.getLogger("nova.linux_net")
|
||||
@@ -707,7 +707,7 @@ def _dnsmasq_cmd(net):
|
||||
'--listen-address=%s' % net['gateway'],
|
||||
'--except-interface=lo',
|
||||
'--dhcp-range=%s,static,120s' % net['dhcp_start'],
|
||||
'--dhcp-lease-max=%s' % IP(net['cidr']).len(),
|
||||
'--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])),
|
||||
'--dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'),
|
||||
'--dhcp-script=%s' % FLAGS.dhcpbridge,
|
||||
'--leasefile-ro']
|
||||
|
||||
+23
-23
@@ -45,10 +45,9 @@ topologies. All of the network commands are issued to a subclass of
|
||||
|
||||
import datetime
|
||||
import math
|
||||
import netaddr
|
||||
import socket
|
||||
|
||||
import IPy
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
@@ -295,8 +294,8 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
def create_networks(self, context, cidr, num_networks, network_size,
|
||||
cidr_v6, gateway_v6, label, *args, **kwargs):
|
||||
"""Create networks based on parameters."""
|
||||
fixed_net = IPy.IP(cidr)
|
||||
fixed_net_v6 = IPy.IP(cidr_v6)
|
||||
fixed_net = netaddr.IPNetwork(cidr)
|
||||
fixed_net_v6 = netaddr.IPNetwork(cidr_v6)
|
||||
significant_bits_v6 = 64
|
||||
network_size_v6 = 1 << 64
|
||||
count = 1
|
||||
@@ -305,15 +304,15 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
start_v6 = index * network_size_v6
|
||||
significant_bits = 32 - int(math.log(network_size, 2))
|
||||
cidr = '%s/%s' % (fixed_net[start], significant_bits)
|
||||
project_net = IPy.IP(cidr)
|
||||
project_net = netaddr.IPNetwork(cidr)
|
||||
net = {}
|
||||
net['bridge'] = FLAGS.flat_network_bridge
|
||||
net['dns'] = FLAGS.flat_network_dns
|
||||
net['cidr'] = cidr
|
||||
net['netmask'] = str(project_net.netmask())
|
||||
net['gateway'] = str(project_net[1])
|
||||
net['broadcast'] = str(project_net.broadcast())
|
||||
net['dhcp_start'] = str(project_net[2])
|
||||
net['netmask'] = str(project_net.netmask)
|
||||
net['gateway'] = str(list(project_net)[1])
|
||||
net['broadcast'] = str(project_net.broadcast)
|
||||
net['dhcp_start'] = str(list(project_net)[2])
|
||||
if num_networks > 1:
|
||||
net['label'] = '%s_%d' % (label, count)
|
||||
else:
|
||||
@@ -324,15 +323,16 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
cidr_v6 = '%s/%s' % (fixed_net_v6[start_v6],
|
||||
significant_bits_v6)
|
||||
net['cidr_v6'] = cidr_v6
|
||||
project_net_v6 = IPy.IP(cidr_v6)
|
||||
|
||||
project_net_v6 = netaddr.IPNetwork(cidr_v6)
|
||||
|
||||
if gateway_v6:
|
||||
# use a pre-defined gateway if one is provided
|
||||
net['gateway_v6'] = str(gateway_v6)
|
||||
net['gateway_v6'] = str(list(gateway_v6)[1])
|
||||
else:
|
||||
net['gateway_v6'] = str(project_net_v6[1])
|
||||
net['gateway_v6'] = str(list(project_net_v6)[1])
|
||||
|
||||
net['netmask_v6'] = str(project_net_v6.prefixlen())
|
||||
net['netmask_v6'] = str(project_net_v6._prefixlen)
|
||||
|
||||
network_ref = self.db.network_create_safe(context, net)
|
||||
|
||||
@@ -356,7 +356,7 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
# to properties of the manager class?
|
||||
bottom_reserved = self._bottom_reserved_ips
|
||||
top_reserved = self._top_reserved_ips
|
||||
project_net = IPy.IP(network_ref['cidr'])
|
||||
project_net = netaddr.IPNetwork(network_ref['cidr'])
|
||||
num_ips = len(project_net)
|
||||
for index in range(num_ips):
|
||||
address = str(project_net[index])
|
||||
@@ -546,13 +546,13 @@ class VlanManager(NetworkManager):
|
||||
' the vlan start cannot be greater'
|
||||
' than 4094'))
|
||||
|
||||
fixed_net = IPy.IP(cidr)
|
||||
if fixed_net.len() < num_networks * network_size:
|
||||
fixed_net = netaddr.IPNetwork(cidr)
|
||||
if len(fixed_net) < num_networks * network_size:
|
||||
raise ValueError(_('The network range is not big enough to fit '
|
||||
'%(num_networks)s. Network size is %(network_size)s' %
|
||||
locals()))
|
||||
|
||||
fixed_net_v6 = IPy.IP(cidr_v6)
|
||||
fixed_net_v6 = netaddr.IPNetwork(cidr_v6)
|
||||
network_size_v6 = 1 << 64
|
||||
significant_bits_v6 = 64
|
||||
for index in range(num_networks):
|
||||
@@ -561,14 +561,14 @@ class VlanManager(NetworkManager):
|
||||
start_v6 = index * network_size_v6
|
||||
significant_bits = 32 - int(math.log(network_size, 2))
|
||||
cidr = "%s/%s" % (fixed_net[start], significant_bits)
|
||||
project_net = IPy.IP(cidr)
|
||||
project_net = netaddr.IPNetwork(cidr)
|
||||
net = {}
|
||||
net['cidr'] = cidr
|
||||
net['netmask'] = str(project_net.netmask())
|
||||
net['gateway'] = str(project_net[1])
|
||||
net['broadcast'] = str(project_net.broadcast())
|
||||
net['vpn_private_address'] = str(project_net[2])
|
||||
net['dhcp_start'] = str(project_net[3])
|
||||
net['netmask'] = str(project_net.netmask)
|
||||
net['gateway'] = str(list(project_net)[1])
|
||||
net['broadcast'] = str(project_net.broadcast)
|
||||
net['vpn_private_address'] = str(list(project_net)[2])
|
||||
net['dhcp_start'] = str(list(project_net)[3])
|
||||
net['vlan'] = vlan
|
||||
net['bridge'] = 'br%s' % vlan
|
||||
if(FLAGS.use_ipv6):
|
||||
|
||||
@@ -56,8 +56,10 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
|
||||
'other_config': {}}
|
||||
network_ref = session.call_xenapi('network.create', network_rec)
|
||||
# 2 - find PIF for VLAN
|
||||
expr = "field 'device' = '%s' and \
|
||||
field 'VLAN' = '-1'" % FLAGS.vlan_interface
|
||||
# NOTE(salvatore-orlando): using double quotes inside single quotes
|
||||
# as xapi filter only support tokens in double quotes
|
||||
expr = 'field "device" = "%s" and \
|
||||
field "VLAN" = "-1"' % FLAGS.vlan_interface
|
||||
pifs = session.call_xenapi('PIF.get_all_records_where', expr)
|
||||
pif_ref = None
|
||||
# Multiple PIF are ok: we are dealing with a pool
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# 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 json
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
NOTIFICATIONS = []
|
||||
|
||||
|
||||
def notify(message):
|
||||
"""Test notifier, stores notifications in memory for unittests."""
|
||||
NOTIFICATIONS.append(message)
|
||||
@@ -93,6 +93,26 @@ class InstanceTypeFilter(HostFilter):
|
||||
"""Use instance_type to filter hosts."""
|
||||
return (self._full_name(), instance_type)
|
||||
|
||||
def _satisfies_extra_specs(self, capabilities, instance_type):
|
||||
"""Check that the capabilities provided by the compute service
|
||||
satisfy the extra specs associated with the instance type"""
|
||||
|
||||
if 'extra_specs' not in instance_type:
|
||||
return True
|
||||
|
||||
# Note(lorinh): For now, we are just checking exact matching on the
|
||||
# values. Later on, we want to handle numerical
|
||||
# values so we can represent things like number of GPU cards
|
||||
|
||||
try:
|
||||
for key, value in instance_type['extra_specs'].iteritems():
|
||||
if capabilities[key] != value:
|
||||
return False
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def filter_hosts(self, zone_manager, query):
|
||||
"""Return a list of hosts that can create instance_type."""
|
||||
instance_type = query
|
||||
@@ -103,7 +123,11 @@ class InstanceTypeFilter(HostFilter):
|
||||
disk_bytes = capabilities['disk_available']
|
||||
spec_ram = instance_type['memory_mb']
|
||||
spec_disk = instance_type['local_gb']
|
||||
if host_ram_mb >= spec_ram and disk_bytes >= spec_disk:
|
||||
extra_specs = instance_type['extra_specs']
|
||||
|
||||
if host_ram_mb >= spec_ram and \
|
||||
disk_bytes >= spec_disk and \
|
||||
self._satisfies_extra_specs(capabilities, instance_type):
|
||||
selected_hosts.append((host, capabilities))
|
||||
return selected_hosts
|
||||
|
||||
|
||||
+101
-76
@@ -19,10 +19,12 @@
|
||||
|
||||
"""Generic Node baseclass for all workers that run on hosts."""
|
||||
|
||||
import greenlet
|
||||
import inspect
|
||||
import multiprocessing
|
||||
import os
|
||||
|
||||
import greenlet
|
||||
|
||||
from eventlet import greenthread
|
||||
|
||||
from nova import context
|
||||
@@ -36,6 +38,8 @@ from nova import version
|
||||
from nova import wsgi
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.service')
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_integer('report_interval', 10,
|
||||
'seconds between nodes reporting state to datastore',
|
||||
@@ -53,6 +57,63 @@ flags.DEFINE_string('api_paste_config', "api-paste.ini",
|
||||
'File name for the paste.deploy config for nova-api')
|
||||
|
||||
|
||||
class Launcher(object):
|
||||
"""Launch one or more services and wait for them to complete."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the service launcher.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
self._services = []
|
||||
|
||||
@staticmethod
|
||||
def run_service(service):
|
||||
"""Start and wait for a service to finish.
|
||||
|
||||
:param service: Service to run and wait for.
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
service.start()
|
||||
try:
|
||||
service.wait()
|
||||
except KeyboardInterrupt:
|
||||
service.stop()
|
||||
|
||||
def launch_service(self, service):
|
||||
"""Load and start the given service.
|
||||
|
||||
:param service: The service you would like to start.
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
process = multiprocessing.Process(target=self.run_service,
|
||||
args=(service,))
|
||||
process.start()
|
||||
self._services.append(process)
|
||||
|
||||
def stop(self):
|
||||
"""Stop all services which are currently running.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
for service in self._services:
|
||||
if service.is_alive():
|
||||
service.terminate()
|
||||
|
||||
def wait(self):
|
||||
"""Waits until all services have been stopped, and then returns.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
for service in self._services:
|
||||
service.join()
|
||||
|
||||
|
||||
class Service(object):
|
||||
"""Base class for workers that run on hosts."""
|
||||
|
||||
@@ -232,45 +293,54 @@ class Service(object):
|
||||
logging.exception(_('model server went away'))
|
||||
|
||||
|
||||
class WsgiService(object):
|
||||
"""Base class for WSGI based services.
|
||||
class WSGIService(object):
|
||||
"""Provides ability to launch API from a 'paste' configuration."""
|
||||
|
||||
For each api you define, you must also define these flags:
|
||||
:<api>_listen: The address on which to listen
|
||||
:<api>_listen_port: The port on which to listen
|
||||
def __init__(self, name, loader=None):
|
||||
"""Initialize, but do not start the WSGI service.
|
||||
|
||||
"""
|
||||
:param name: The name of the WSGI service given to the loader.
|
||||
:param loader: Loads the WSGI application using the given name.
|
||||
:returns: None
|
||||
|
||||
def __init__(self, conf, apis):
|
||||
self.conf = conf
|
||||
self.apis = apis
|
||||
self.wsgi_app = None
|
||||
"""
|
||||
self.name = name
|
||||
self.loader = loader or wsgi.Loader()
|
||||
self.app = self.loader.load_app(name)
|
||||
self.host = getattr(FLAGS, '%s_listen' % name, "0.0.0.0")
|
||||
self.port = getattr(FLAGS, '%s_listen_port' % name, 0)
|
||||
self.server = wsgi.Server(name,
|
||||
self.app,
|
||||
host=self.host,
|
||||
port=self.port)
|
||||
|
||||
def start(self):
|
||||
self.wsgi_app = _run_wsgi(self.conf, self.apis)
|
||||
"""Start serving this service using loaded configuration.
|
||||
|
||||
Also, retrieve updated port number in case '0' was passed in, which
|
||||
indicates a random port should be used.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
self.server.start()
|
||||
self.port = self.server.port
|
||||
|
||||
def stop(self):
|
||||
"""Stop serving this API.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
self.server.stop()
|
||||
|
||||
def wait(self):
|
||||
self.wsgi_app.wait()
|
||||
"""Wait for the service to stop serving this API.
|
||||
|
||||
def get_socket_info(self, api_name):
|
||||
"""Returns the (host, port) that an API was started on."""
|
||||
return self.wsgi_app.socket_info[api_name]
|
||||
:returns: None
|
||||
|
||||
|
||||
class ApiService(WsgiService):
|
||||
"""Class for our nova-api service."""
|
||||
|
||||
@classmethod
|
||||
def create(cls, conf=None):
|
||||
if not conf:
|
||||
conf = wsgi.paste_config_file(FLAGS.api_paste_config)
|
||||
if not conf:
|
||||
message = (_('No paste configuration found for: %s'),
|
||||
FLAGS.api_paste_config)
|
||||
raise exception.Error(message)
|
||||
api_endpoints = ['ec2', 'osapi']
|
||||
service = cls(conf, api_endpoints)
|
||||
return service
|
||||
"""
|
||||
self.server.wait()
|
||||
|
||||
|
||||
def serve(*services):
|
||||
@@ -302,48 +372,3 @@ def serve(*services):
|
||||
def wait():
|
||||
while True:
|
||||
greenthread.sleep(5)
|
||||
|
||||
|
||||
def serve_wsgi(cls, conf=None):
|
||||
try:
|
||||
service = cls.create(conf)
|
||||
except Exception:
|
||||
logging.exception('in WsgiService.create()')
|
||||
raise
|
||||
finally:
|
||||
# After we've loaded up all our dynamic bits, check
|
||||
# whether we should print help
|
||||
flags.DEFINE_flag(flags.HelpFlag())
|
||||
flags.DEFINE_flag(flags.HelpshortFlag())
|
||||
flags.DEFINE_flag(flags.HelpXMLFlag())
|
||||
FLAGS.ParseNewFlags()
|
||||
|
||||
service.start()
|
||||
|
||||
return service
|
||||
|
||||
|
||||
def _run_wsgi(paste_config_file, apis):
|
||||
logging.debug(_('Using paste.deploy config at: %s'), paste_config_file)
|
||||
apps = []
|
||||
for api in apis:
|
||||
config = wsgi.load_paste_configuration(paste_config_file, api)
|
||||
if config is None:
|
||||
logging.debug(_('No paste configuration for app: %s'), api)
|
||||
continue
|
||||
logging.debug(_('App Config: %(api)s\n%(config)r') % locals())
|
||||
logging.info(_('Running %s API'), api)
|
||||
app = wsgi.load_paste_app(paste_config_file, api)
|
||||
apps.append((app,
|
||||
getattr(FLAGS, '%s_listen_port' % api),
|
||||
getattr(FLAGS, '%s_listen' % api),
|
||||
api))
|
||||
if len(apps) == 0:
|
||||
logging.error(_('No known API applications configured in %s.'),
|
||||
paste_config_file)
|
||||
return
|
||||
|
||||
server = wsgi.Server()
|
||||
for app in apps:
|
||||
server.start(*app)
|
||||
return server
|
||||
|
||||
@@ -38,7 +38,6 @@ from nova import flags
|
||||
from nova import rpc
|
||||
from nova import utils
|
||||
from nova import service
|
||||
from nova import wsgi
|
||||
from nova.virt import fake
|
||||
|
||||
|
||||
@@ -81,7 +80,6 @@ class TestCase(unittest.TestCase):
|
||||
self.injected = []
|
||||
self._services = []
|
||||
self._monkey_patch_attach()
|
||||
self._monkey_patch_wsgi()
|
||||
self._original_flags = FLAGS.FlagValuesDict()
|
||||
rpc.ConnectionPool = rpc.Pool(max_size=FLAGS.rpc_conn_pool_size)
|
||||
|
||||
@@ -107,7 +105,6 @@ class TestCase(unittest.TestCase):
|
||||
|
||||
# Reset our monkey-patches
|
||||
rpc.Consumer.attach_to_eventlet = self.original_attach
|
||||
wsgi.Server.start = self.original_start
|
||||
|
||||
# Stop any timers
|
||||
for x in self.injected:
|
||||
@@ -163,26 +160,6 @@ class TestCase(unittest.TestCase):
|
||||
_wrapped.func_name = self.original_attach.func_name
|
||||
rpc.Consumer.attach_to_eventlet = _wrapped
|
||||
|
||||
def _monkey_patch_wsgi(self):
|
||||
"""Allow us to kill servers spawned by wsgi.Server."""
|
||||
self.original_start = wsgi.Server.start
|
||||
|
||||
@functools.wraps(self.original_start)
|
||||
def _wrapped_start(inner_self, *args, **kwargs):
|
||||
original_spawn_n = inner_self.pool.spawn_n
|
||||
|
||||
@functools.wraps(original_spawn_n)
|
||||
def _wrapped_spawn_n(*args, **kwargs):
|
||||
rv = greenthread.spawn(*args, **kwargs)
|
||||
self._services.append(rv)
|
||||
|
||||
inner_self.pool.spawn_n = _wrapped_spawn_n
|
||||
self.original_start(inner_self, *args, **kwargs)
|
||||
inner_self.pool.spawn_n = original_spawn_n
|
||||
|
||||
_wrapped_start.func_name = self.original_start.func_name
|
||||
wsgi.Server.start = _wrapped_start
|
||||
|
||||
# Useful assertions
|
||||
def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
|
||||
"""Assert two dicts are equivalent.
|
||||
|
||||
@@ -50,7 +50,7 @@ def setup():
|
||||
|
||||
testdb = os.path.join(FLAGS.state_path, FLAGS.sqlite_db)
|
||||
if os.path.exists(testdb):
|
||||
os.unlink(testdb)
|
||||
return
|
||||
migration.db_sync()
|
||||
ctxt = context.get_admin_context()
|
||||
network_manager.VlanManager().create_networks(ctxt,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC
|
||||
#
|
||||
# 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.
|
||||
@@ -0,0 +1,186 @@
|
||||
# Copyright 2011 Eldar Nugaev
|
||||
# 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 json
|
||||
import stubout
|
||||
import webob
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import test
|
||||
from nova import network
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
|
||||
from nova.api.openstack.contrib.floating_ips import FloatingIPController
|
||||
from nova.api.openstack.contrib.floating_ips import _translate_floating_ip_view
|
||||
|
||||
|
||||
def network_api_get_floating_ip(self, context, id):
|
||||
return {'id': 1, 'address': '10.10.10.10',
|
||||
'fixed_ip': {'address': '11.0.0.1'}}
|
||||
|
||||
|
||||
def network_api_list_floating_ips(self, context):
|
||||
return [{'id': 1,
|
||||
'address': '10.10.10.10',
|
||||
'instance': {'id': 11},
|
||||
'fixed_ip': {'address': '10.0.0.1'}},
|
||||
{'id': 2,
|
||||
'address': '10.10.10.11'}]
|
||||
|
||||
|
||||
def network_api_allocate(self, context):
|
||||
return '10.10.10.10'
|
||||
|
||||
|
||||
def network_api_release(self, context, address):
|
||||
pass
|
||||
|
||||
|
||||
def network_api_associate(self, context, floating_ip, fixed_ip):
|
||||
pass
|
||||
|
||||
|
||||
def network_api_disassociate(self, context, floating_address):
|
||||
pass
|
||||
|
||||
|
||||
class FloatingIpTest(test.TestCase):
|
||||
address = "10.10.10.10"
|
||||
|
||||
def _create_floating_ip(self):
|
||||
"""Create a floating ip object."""
|
||||
host = "fake_host"
|
||||
return db.floating_ip_create(self.context,
|
||||
{'address': self.address,
|
||||
'host': host})
|
||||
|
||||
def _delete_floating_ip(self):
|
||||
db.floating_ip_destroy(self.context, self.address)
|
||||
|
||||
def setUp(self):
|
||||
super(FloatingIpTest, self).setUp()
|
||||
self.controller = FloatingIPController()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
self.stubs.Set(network.api.API, "get_floating_ip",
|
||||
network_api_get_floating_ip)
|
||||
self.stubs.Set(network.api.API, "list_floating_ips",
|
||||
network_api_list_floating_ips)
|
||||
self.stubs.Set(network.api.API, "allocate_floating_ip",
|
||||
network_api_allocate)
|
||||
self.stubs.Set(network.api.API, "release_floating_ip",
|
||||
network_api_release)
|
||||
self.stubs.Set(network.api.API, "associate_floating_ip",
|
||||
network_api_associate)
|
||||
self.stubs.Set(network.api.API, "disassociate_floating_ip",
|
||||
network_api_disassociate)
|
||||
self.context = context.get_admin_context()
|
||||
self._create_floating_ip()
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
self._delete_floating_ip()
|
||||
super(FloatingIpTest, self).tearDown()
|
||||
|
||||
def test_translate_floating_ip_view(self):
|
||||
floating_ip_address = self._create_floating_ip()
|
||||
floating_ip = db.floating_ip_get_by_address(self.context,
|
||||
floating_ip_address)
|
||||
view = _translate_floating_ip_view(floating_ip)
|
||||
self.assertTrue('floating_ip' in view)
|
||||
self.assertTrue(view['floating_ip']['id'])
|
||||
self.assertEqual(view['floating_ip']['ip'], self.address)
|
||||
self.assertEqual(view['floating_ip']['fixed_ip'], None)
|
||||
self.assertEqual(view['floating_ip']['instance_id'], None)
|
||||
|
||||
def test_floating_ips_list(self):
|
||||
req = webob.Request.blank('/v1.1/os-floating-ips')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
res_dict = json.loads(res.body)
|
||||
response = {'floating_ips': [{'floating_ip': {'instance_id': 11,
|
||||
'ip': '10.10.10.10',
|
||||
'fixed_ip': '10.0.0.1',
|
||||
'id': 1}},
|
||||
{'floating_ip': {'instance_id': None,
|
||||
'ip': '10.10.10.11',
|
||||
'fixed_ip': None,
|
||||
'id': 2}}]}
|
||||
self.assertEqual(res_dict, response)
|
||||
|
||||
def test_floating_ip_show(self):
|
||||
req = webob.Request.blank('/v1.1/os-floating-ips/1')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(res_dict['floating_ip']['id'], 1)
|
||||
self.assertEqual(res_dict['floating_ip']['ip'], '10.10.10.10')
|
||||
self.assertEqual(res_dict['floating_ip']['fixed_ip'], '11.0.0.1')
|
||||
self.assertEqual(res_dict['floating_ip']['instance_id'], None)
|
||||
|
||||
def test_floating_ip_allocate(self):
|
||||
req = webob.Request.blank('/v1.1/os-floating-ips')
|
||||
req.method = 'POST'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
ip = json.loads(res.body)['allocated']
|
||||
expected = {
|
||||
"id": 1,
|
||||
"floating_ip": '10.10.10.10'}
|
||||
self.assertEqual(ip, expected)
|
||||
|
||||
def test_floating_ip_release(self):
|
||||
req = webob.Request.blank('/v1.1/os-floating-ips/1')
|
||||
req.method = 'DELETE'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
actual = json.loads(res.body)['released']
|
||||
expected = {
|
||||
"id": 1,
|
||||
"floating_ip": '10.10.10.10'}
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_floating_ip_associate(self):
|
||||
body = dict(associate_address=dict(fixed_ip='1.2.3.4'))
|
||||
req = webob.Request.blank('/v1.1/os-floating-ips/1/associate')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
actual = json.loads(res.body)['associated']
|
||||
expected = {
|
||||
"floating_ip_id": '1',
|
||||
"floating_ip": "10.10.10.10",
|
||||
"fixed_ip": "1.2.3.4"}
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_floating_ip_disassociate(self):
|
||||
req = webob.Request.blank('/v1.1/os-floating-ips/1/disassociate')
|
||||
req.method = 'POST'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
ip = json.loads(res.body)['disassociated']
|
||||
expected = {
|
||||
"floating_ip": '10.10.10.10',
|
||||
"fixed_ip": '11.0.0.1'}
|
||||
self.assertEqual(ip, expected)
|
||||
@@ -0,0 +1,198 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 University of Southern California
|
||||
# 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 json
|
||||
import stubout
|
||||
import unittest
|
||||
import webob
|
||||
import os.path
|
||||
|
||||
|
||||
from nova import flags
|
||||
from nova.api import openstack
|
||||
from nova.api.openstack import auth
|
||||
from nova.api.openstack import extensions
|
||||
from nova.tests.api.openstack import fakes
|
||||
import nova.wsgi
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
def return_create_flavor_extra_specs(context, flavor_id, extra_specs):
|
||||
return stub_flavor_extra_specs()
|
||||
|
||||
|
||||
def return_flavor_extra_specs(context, flavor_id):
|
||||
return stub_flavor_extra_specs()
|
||||
|
||||
|
||||
def return_flavor_extra_specs(context, flavor_id):
|
||||
return stub_flavor_extra_specs()
|
||||
|
||||
|
||||
def return_empty_flavor_extra_specs(context, flavor_id):
|
||||
return {}
|
||||
|
||||
|
||||
def delete_flavor_extra_specs(context, flavor_id, key):
|
||||
pass
|
||||
|
||||
|
||||
def stub_flavor_extra_specs():
|
||||
specs = {
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
"key4": "value4",
|
||||
"key5": "value5"}
|
||||
return specs
|
||||
|
||||
|
||||
class FlavorsExtraSpecsTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FlavorsExtraSpecsTest, self).setUp()
|
||||
FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__),
|
||||
"extensions")
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.auth_data = {}
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.mware = auth.AuthMiddleware(
|
||||
extensions.ExtensionMiddleware(
|
||||
openstack.APIRouterV11()))
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
super(FlavorsExtraSpecsTest, self).tearDown()
|
||||
|
||||
def test_index(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_flavor_extra_specs)
|
||||
request = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
res = request.get_response(self.mware)
|
||||
self.assertEqual(200, res.status_int)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
self.assertEqual('value1', res_dict['extra_specs']['key1'])
|
||||
|
||||
def test_index_no_data(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_empty_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
res = req.get_response(self.mware)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
self.assertEqual(0, len(res_dict['extra_specs']))
|
||||
|
||||
def test_show(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key5')
|
||||
res = req.get_response(self.mware)
|
||||
self.assertEqual(200, res.status_int)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
self.assertEqual('value5', res_dict['key5'])
|
||||
|
||||
def test_show_spec_not_found(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_empty_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key6')
|
||||
res = req.get_response(self.mware)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(404, res.status_int)
|
||||
|
||||
def test_delete(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_delete',
|
||||
delete_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key5')
|
||||
req.method = 'DELETE'
|
||||
res = req.get_response(self.mware)
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
req.method = 'POST'
|
||||
req.body = '{"extra_specs": {"key1": "value1"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
self.assertEqual('value1', res_dict['extra_specs']['key1'])
|
||||
|
||||
def test_create_empty_body(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"key1": "value1"}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual('value1', res_dict['key1'])
|
||||
|
||||
def test_update_item_empty_body(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
|
||||
req.method = 'PUT'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"key1": "value1", "key2": "value2"}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/bad')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"key1": "value1"}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
self.assertEqual(400, res.status_int)
|
||||
@@ -16,7 +16,6 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import json
|
||||
import random
|
||||
import string
|
||||
|
||||
@@ -29,11 +28,11 @@ from glance.common import exception as glance_exc
|
||||
|
||||
from nova import context
|
||||
from nova import exception as exc
|
||||
from nova import flags
|
||||
from nova import utils
|
||||
import nova.api.openstack.auth
|
||||
from nova.api import openstack
|
||||
from nova.api.openstack import auth
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import versions
|
||||
from nova.api.openstack import limits
|
||||
from nova.auth.manager import User, Project
|
||||
@@ -82,7 +81,8 @@ def wsgi_app(inner_app10=None, inner_app11=None):
|
||||
api10 = openstack.FaultWrapper(auth.AuthMiddleware(
|
||||
limits.RateLimitingMiddleware(inner_app10)))
|
||||
api11 = openstack.FaultWrapper(auth.AuthMiddleware(
|
||||
limits.RateLimitingMiddleware(inner_app11)))
|
||||
limits.RateLimitingMiddleware(
|
||||
extensions.ExtensionMiddleware(inner_app11))))
|
||||
mapper['/v1.0'] = api10
|
||||
mapper['/v1.1'] = api11
|
||||
mapper['/'] = openstack.FaultWrapper(versions.Versions())
|
||||
|
||||
@@ -38,6 +38,7 @@ class ImageMetaDataTest(unittest.TestCase):
|
||||
'name': 'image1',
|
||||
'deleted': False,
|
||||
'container_format': None,
|
||||
'checksum': None,
|
||||
'created_at': '2011-03-22T17:40:15',
|
||||
'disk_format': None,
|
||||
'updated_at': '2011-03-22T17:40:15',
|
||||
@@ -53,6 +54,7 @@ class ImageMetaDataTest(unittest.TestCase):
|
||||
'name': 'image2',
|
||||
'deleted': False,
|
||||
'container_format': None,
|
||||
'checksum': None,
|
||||
'created_at': '2011-03-22T17:40:15',
|
||||
'disk_format': None,
|
||||
'updated_at': '2011-03-22T17:40:15',
|
||||
@@ -68,6 +70,7 @@ class ImageMetaDataTest(unittest.TestCase):
|
||||
'name': 'image3',
|
||||
'deleted': False,
|
||||
'container_format': None,
|
||||
'checksum': None,
|
||||
'created_at': '2011-03-22T17:40:15',
|
||||
'disk_format': None,
|
||||
'updated_at': '2011-03-22T17:40:15',
|
||||
@@ -104,7 +107,10 @@ class ImageMetaDataTest(unittest.TestCase):
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('value1', res_dict['metadata']['key1'])
|
||||
expected = self.IMAGE_FIXTURES[0]['properties']
|
||||
self.assertEqual(len(expected), len(res_dict['metadata']))
|
||||
for (key, value) in res_dict['metadata'].items():
|
||||
self.assertEqual(value, res_dict['metadata'][key])
|
||||
|
||||
def test_index_xml(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
|
||||
@@ -171,16 +171,10 @@ class _IntegratedTestBase(test.TestCase):
|
||||
self.api = self.user.openstack_api
|
||||
|
||||
def _start_api_service(self):
|
||||
api_service = service.ApiService.create()
|
||||
api_service.start()
|
||||
|
||||
if not api_service:
|
||||
raise Exception("API Service was None")
|
||||
|
||||
self.api_service = api_service
|
||||
|
||||
host, port = api_service.get_socket_info('osapi')
|
||||
self.auth_url = 'http://%s:%s/v1.1' % (host, port)
|
||||
osapi = service.WSGIService("osapi")
|
||||
osapi.start()
|
||||
self.auth_url = 'http://%s:%s/v1.1' % (osapi.host, osapi.port)
|
||||
LOG.warn(self.auth_url)
|
||||
|
||||
def tearDown(self):
|
||||
self.context.cleanup()
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"""
|
||||
Base class of Unit Tests for all network models
|
||||
"""
|
||||
import IPy
|
||||
import netaddr
|
||||
import os
|
||||
|
||||
from nova import context
|
||||
|
||||
@@ -67,7 +67,18 @@ class HostFilterTestCase(test.TestCase):
|
||||
flavorid=1,
|
||||
swap=500,
|
||||
rxtx_quota=30000,
|
||||
rxtx_cap=200)
|
||||
rxtx_cap=200,
|
||||
extra_specs={})
|
||||
self.gpu_instance_type = dict(name='tiny.gpu',
|
||||
memory_mb=50,
|
||||
vcpus=10,
|
||||
local_gb=500,
|
||||
flavorid=2,
|
||||
swap=500,
|
||||
rxtx_quota=30000,
|
||||
rxtx_cap=200,
|
||||
extra_specs={'xpu_arch': 'fermi',
|
||||
'xpu_info': 'Tesla 2050'})
|
||||
|
||||
self.zone_manager = FakeZoneManager()
|
||||
states = {}
|
||||
@@ -75,6 +86,18 @@ class HostFilterTestCase(test.TestCase):
|
||||
states['host%02d' % (x + 1)] = {'compute': self._host_caps(x)}
|
||||
self.zone_manager.service_states = states
|
||||
|
||||
# Add some extra capabilities to some hosts
|
||||
host07 = self.zone_manager.service_states['host07']['compute']
|
||||
host07['xpu_arch'] = 'fermi'
|
||||
host07['xpu_info'] = 'Tesla 2050'
|
||||
|
||||
host08 = self.zone_manager.service_states['host08']['compute']
|
||||
host08['xpu_arch'] = 'radeon'
|
||||
|
||||
host09 = self.zone_manager.service_states['host09']['compute']
|
||||
host09['xpu_arch'] = 'fermi'
|
||||
host09['xpu_info'] = 'Tesla 2150'
|
||||
|
||||
def tearDown(self):
|
||||
FLAGS.default_host_filter = self.old_flag
|
||||
|
||||
@@ -116,6 +139,17 @@ class HostFilterTestCase(test.TestCase):
|
||||
self.assertEquals('host05', just_hosts[0])
|
||||
self.assertEquals('host10', just_hosts[5])
|
||||
|
||||
def test_instance_type_filter_extra_specs(self):
|
||||
hf = host_filter.InstanceTypeFilter()
|
||||
# filter all hosts that can support 50 ram and 500 disk
|
||||
name, cooked = hf.instance_type_to_filter(self.gpu_instance_type)
|
||||
self.assertEquals('nova.scheduler.host_filter.InstanceTypeFilter',
|
||||
name)
|
||||
hosts = hf.filter_hosts(self.zone_manager, cooked)
|
||||
self.assertEquals(1, len(hosts))
|
||||
just_hosts = [host for host, caps in hosts]
|
||||
self.assertEquals('host07', just_hosts[0])
|
||||
|
||||
def test_json_filter(self):
|
||||
hf = host_filter.JsonFilter()
|
||||
# filter all hosts that can support 50 ram and 500 disk
|
||||
|
||||
@@ -85,5 +85,27 @@ class AdminApiTestCase(test.TestCase):
|
||||
def test_block_external_ips(self):
|
||||
"""Make sure provider firewall rules are created."""
|
||||
result = self.api.block_external_addresses(self.context, '1.1.1.1/32')
|
||||
self.api.remove_external_address_block(self.context, '1.1.1.1/32')
|
||||
self.assertEqual('OK', result['status'])
|
||||
self.assertEqual('Added 3 rules', result['message'])
|
||||
|
||||
def test_list_blocked_ips(self):
|
||||
"""Make sure we can see the external blocks that exist."""
|
||||
self.api.block_external_addresses(self.context, '1.1.1.2/32')
|
||||
result = self.api.describe_external_address_blocks(self.context)
|
||||
num = len(db.provider_fw_rule_get_all(self.context))
|
||||
self.api.remove_external_address_block(self.context, '1.1.1.2/32')
|
||||
# we only list IP, not tcp/udp/icmp rules
|
||||
self.assertEqual(num / 3, len(result['externalIpBlockInfo']))
|
||||
|
||||
def test_remove_ip_block(self):
|
||||
"""Remove ip blocks."""
|
||||
result = self.api.block_external_addresses(self.context, '1.1.1.3/32')
|
||||
self.assertEqual('OK', result['status'])
|
||||
num0 = len(db.provider_fw_rule_get_all(self.context))
|
||||
result = self.api.remove_external_address_block(self.context,
|
||||
'1.1.1.3/32')
|
||||
self.assertEqual('OK', result['status'])
|
||||
self.assertEqual('Deleted 3 rules', result['message'])
|
||||
num1 = len(db.provider_fw_rule_get_all(self.context))
|
||||
self.assert_(num1 < num0)
|
||||
|
||||
@@ -25,6 +25,7 @@ from nova import log as logging
|
||||
from nova import test
|
||||
from nova.auth import manager
|
||||
from nova.api.ec2 import cloud
|
||||
from nova.auth import fakeldap
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger('nova.tests.auth_unittest')
|
||||
@@ -369,6 +370,15 @@ class _AuthManagerBaseTestCase(test.TestCase):
|
||||
class AuthManagerLdapTestCase(_AuthManagerBaseTestCase):
|
||||
auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
|
||||
|
||||
def test_reconnect_on_server_failure(self):
|
||||
self.manager.get_users()
|
||||
fakeldap.server_fail = True
|
||||
try:
|
||||
self.assertRaises(fakeldap.SERVER_DOWN, self.manager.get_users)
|
||||
finally:
|
||||
fakeldap.server_fail = False
|
||||
self.manager.get_users()
|
||||
|
||||
|
||||
class AuthManagerDbTestCase(_AuthManagerBaseTestCase):
|
||||
auth_driver = 'nova.auth.dbdriver.DbDriver'
|
||||
|
||||
@@ -37,6 +37,7 @@ from nova import log as logging
|
||||
from nova import rpc
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.notifier import test_notifier
|
||||
|
||||
LOG = logging.getLogger('nova.tests.compute')
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -62,6 +63,7 @@ class ComputeTestCase(test.TestCase):
|
||||
super(ComputeTestCase, self).setUp()
|
||||
self.flags(connection_type='fake',
|
||||
stub_network=True,
|
||||
notification_driver='nova.notifier.test_notifier',
|
||||
network_manager='nova.network.manager.FlatManager')
|
||||
self.compute = utils.import_object(FLAGS.compute_manager)
|
||||
self.compute_api = compute.API()
|
||||
@@ -69,6 +71,7 @@ class ComputeTestCase(test.TestCase):
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.RequestContext('fake', 'fake', False)
|
||||
test_notifier.NOTIFICATIONS = []
|
||||
|
||||
def fake_show(meh, context, id):
|
||||
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}}
|
||||
@@ -327,6 +330,50 @@ class ComputeTestCase(test.TestCase):
|
||||
self.assert_(console)
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_run_instance_usage_notification(self):
|
||||
"""Ensure run instance generates apropriate usage notification"""
|
||||
instance_id = self._create_instance()
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
msg = test_notifier.NOTIFICATIONS[0]
|
||||
self.assertEquals(msg['priority'], 'INFO')
|
||||
self.assertEquals(msg['event_type'], 'compute.instance.create')
|
||||
payload = msg['payload']
|
||||
self.assertEquals(payload['tenant_id'], self.project.id)
|
||||
self.assertEquals(payload['user_id'], self.user.id)
|
||||
self.assertEquals(payload['instance_id'], instance_id)
|
||||
self.assertEquals(payload['instance_type'], 'm1.tiny')
|
||||
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
self.assertEquals(str(payload['instance_type_id']), str(type_id))
|
||||
self.assertTrue('display_name' in payload)
|
||||
self.assertTrue('created_at' in payload)
|
||||
self.assertTrue('launched_at' in payload)
|
||||
self.assertEquals(payload['image_ref'], '1')
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_terminate_usage_notification(self):
|
||||
"""Ensure terminate_instance generates apropriate usage notification"""
|
||||
instance_id = self._create_instance()
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
test_notifier.NOTIFICATIONS = []
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
msg = test_notifier.NOTIFICATIONS[0]
|
||||
self.assertEquals(msg['priority'], 'INFO')
|
||||
self.assertEquals(msg['event_type'], 'compute.instance.delete')
|
||||
payload = msg['payload']
|
||||
self.assertEquals(payload['tenant_id'], self.project.id)
|
||||
self.assertEquals(payload['user_id'], self.user.id)
|
||||
self.assertEquals(payload['instance_id'], instance_id)
|
||||
self.assertEquals(payload['instance_type'], 'm1.tiny')
|
||||
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
self.assertEquals(str(payload['instance_type_id']), str(type_id))
|
||||
self.assertTrue('display_name' in payload)
|
||||
self.assertTrue('created_at' in payload)
|
||||
self.assertTrue('launched_at' in payload)
|
||||
self.assertEquals(payload['image_ref'], '1')
|
||||
|
||||
def test_run_instance_existing(self):
|
||||
"""Ensure failure when running an instance that already exists"""
|
||||
instance_id = self._create_instance()
|
||||
@@ -378,6 +425,36 @@ class ComputeTestCase(test.TestCase):
|
||||
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_resize_instance_notification(self):
|
||||
"""Ensure notifications on instance migrate/resize"""
|
||||
instance_id = self._create_instance()
|
||||
context = self.context.elevated()
|
||||
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
test_notifier.NOTIFICATIONS = []
|
||||
|
||||
db.instance_update(self.context, instance_id, {'host': 'foo'})
|
||||
self.compute.prep_resize(context, instance_id, 1)
|
||||
migration_ref = db.migration_get_by_instance_and_status(context,
|
||||
instance_id, 'pre-migrating')
|
||||
|
||||
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
|
||||
msg = test_notifier.NOTIFICATIONS[0]
|
||||
self.assertEquals(msg['priority'], 'INFO')
|
||||
self.assertEquals(msg['event_type'], 'compute.instance.resize.prep')
|
||||
payload = msg['payload']
|
||||
self.assertEquals(payload['tenant_id'], self.project.id)
|
||||
self.assertEquals(payload['user_id'], self.user.id)
|
||||
self.assertEquals(payload['instance_id'], instance_id)
|
||||
self.assertEquals(payload['instance_type'], 'm1.tiny')
|
||||
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
self.assertEquals(str(payload['instance_type_id']), str(type_id))
|
||||
self.assertTrue('display_name' in payload)
|
||||
self.assertTrue('created_at' in payload)
|
||||
self.assertTrue('launched_at' in payload)
|
||||
self.assertEquals(payload['image_ref'], '1')
|
||||
self.compute.terminate_instance(context, instance_id)
|
||||
|
||||
def test_resize_instance(self):
|
||||
"""Ensure instance can be migrated/resized"""
|
||||
instance_id = self._create_instance()
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"""
|
||||
Unit Tests for flat network code
|
||||
"""
|
||||
import IPy
|
||||
import netaddr
|
||||
import os
|
||||
import unittest
|
||||
|
||||
@@ -45,8 +45,8 @@ class FlatNetworkTestCase(base.NetworkTestCase):
|
||||
|
||||
self.context._project = self.projects[0]
|
||||
self.context.project_id = self.projects[0].id
|
||||
pubnet = IPy.IP(flags.FLAGS.floating_range)
|
||||
address = str(pubnet[0])
|
||||
pubnet = netaddr.IPRange(flags.FLAGS.floating_range)
|
||||
address = str(list(pubnet)[0])
|
||||
try:
|
||||
db.floating_ip_get_by_address(context.get_admin_context(), address)
|
||||
except exception.NotFound:
|
||||
|
||||
@@ -67,7 +67,8 @@ class HostFilterTestCase(test.TestCase):
|
||||
flavorid=1,
|
||||
swap=500,
|
||||
rxtx_quota=30000,
|
||||
rxtx_cap=200)
|
||||
rxtx_cap=200,
|
||||
extra_specs={})
|
||||
|
||||
self.zone_manager = FakeZoneManager()
|
||||
states = {}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 University of Southern California
|
||||
# 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.
|
||||
"""
|
||||
Unit Tests for instance types extra specs code
|
||||
"""
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import test
|
||||
from nova.db.sqlalchemy.session import get_session
|
||||
from nova.db.sqlalchemy import models
|
||||
|
||||
|
||||
class InstanceTypeExtraSpecsTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(InstanceTypeExtraSpecsTestCase, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
values = dict(name="cg1.4xlarge",
|
||||
memory_mb=22000,
|
||||
vcpus=8,
|
||||
local_gb=1690,
|
||||
flavorid=105)
|
||||
specs = dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus=2,
|
||||
xpu_model="Tesla 2050")
|
||||
values['extra_specs'] = specs
|
||||
ref = db.api.instance_type_create(self.context,
|
||||
values)
|
||||
self.instance_type_id = ref.id
|
||||
|
||||
def tearDown(self):
|
||||
# Remove the instance type from the database
|
||||
db.api.instance_type_purge(context.get_admin_context(), "cg1.4xlarge")
|
||||
super(InstanceTypeExtraSpecsTestCase, self).tearDown()
|
||||
|
||||
def test_instance_type_specs_get(self):
|
||||
expected_specs = dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus="2",
|
||||
xpu_model="Tesla 2050")
|
||||
actual_specs = db.api.instance_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.instance_type_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_instance_type_extra_specs_delete(self):
|
||||
expected_specs = dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus="2")
|
||||
db.api.instance_type_extra_specs_delete(context.get_admin_context(),
|
||||
self.instance_type_id,
|
||||
"xpu_model")
|
||||
actual_specs = db.api.instance_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.instance_type_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_instance_type_extra_specs_update(self):
|
||||
expected_specs = dict(cpu_arch="x86_64",
|
||||
cpu_model="Sandy Bridge",
|
||||
xpu_arch="fermi",
|
||||
xpus="2",
|
||||
xpu_model="Tesla 2050")
|
||||
db.api.instance_type_extra_specs_update_or_create(
|
||||
context.get_admin_context(),
|
||||
self.instance_type_id,
|
||||
dict(cpu_model="Sandy Bridge"))
|
||||
actual_specs = db.api.instance_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.instance_type_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_instance_type_extra_specs_create(self):
|
||||
expected_specs = dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus="2",
|
||||
xpu_model="Tesla 2050",
|
||||
net_arch="ethernet",
|
||||
net_mbps="10000")
|
||||
db.api.instance_type_extra_specs_update_or_create(
|
||||
context.get_admin_context(),
|
||||
self.instance_type_id,
|
||||
dict(net_arch="ethernet",
|
||||
net_mbps=10000))
|
||||
actual_specs = db.api.instance_type_extra_specs_get(
|
||||
context.get_admin_context(),
|
||||
self.instance_type_id)
|
||||
self.assertEquals(expected_specs, actual_specs)
|
||||
|
||||
def test_instance_type_get_by_id_with_extra_specs(self):
|
||||
instance_type = db.api.instance_type_get_by_id(
|
||||
context.get_admin_context(),
|
||||
self.instance_type_id)
|
||||
self.assertEquals(instance_type['extra_specs'],
|
||||
dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus="2",
|
||||
xpu_model="Tesla 2050"))
|
||||
instance_type = db.api.instance_type_get_by_id(
|
||||
context.get_admin_context(),
|
||||
5)
|
||||
self.assertEquals(instance_type['extra_specs'], {})
|
||||
|
||||
def test_instance_type_get_by_name_with_extra_specs(self):
|
||||
instance_type = db.api.instance_type_get_by_name(
|
||||
context.get_admin_context(),
|
||||
"cg1.4xlarge")
|
||||
self.assertEquals(instance_type['extra_specs'],
|
||||
dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus="2",
|
||||
xpu_model="Tesla 2050"))
|
||||
|
||||
instance_type = db.api.instance_type_get_by_name(
|
||||
context.get_admin_context(),
|
||||
"m1.small")
|
||||
self.assertEquals(instance_type['extra_specs'], {})
|
||||
|
||||
def test_instance_type_get_by_id_with_extra_specs(self):
|
||||
instance_type = db.api.instance_type_get_by_flavor_id(
|
||||
context.get_admin_context(),
|
||||
105)
|
||||
self.assertEquals(instance_type['extra_specs'],
|
||||
dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus="2",
|
||||
xpu_model="Tesla 2050"))
|
||||
|
||||
instance_type = db.api.instance_type_get_by_flavor_id(
|
||||
context.get_admin_context(),
|
||||
2)
|
||||
self.assertEquals(instance_type['extra_specs'], {})
|
||||
|
||||
def test_instance_type_get_all(self):
|
||||
specs = dict(cpu_arch="x86_64",
|
||||
cpu_model="Nehalem",
|
||||
xpu_arch="fermi",
|
||||
xpus='2',
|
||||
xpu_model="Tesla 2050")
|
||||
|
||||
types = db.api.instance_type_get_all(context.get_admin_context())
|
||||
|
||||
self.assertEquals(types['cg1.4xlarge']['extra_specs'], specs)
|
||||
self.assertEquals(types['m1.small']['extra_specs'], {})
|
||||
@@ -1115,6 +1115,13 @@ class IptablesFirewallTestCase(test.TestCase):
|
||||
provjump_rules.append(rule)
|
||||
self.assertEqual(1, len(provjump_rules))
|
||||
|
||||
# remove a rule from the db, cast to compute to refresh rule
|
||||
db.provider_fw_rule_destroy(admin_ctxt, provider_fw1['id'])
|
||||
self.fw.refresh_provider_fw_rules()
|
||||
rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules
|
||||
if rule.chain == 'provider']
|
||||
self.assertEqual(1, len(rules))
|
||||
|
||||
|
||||
class NWFilterTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"""
|
||||
Unit Tests for network code
|
||||
"""
|
||||
import IPy
|
||||
import netaddr
|
||||
import os
|
||||
|
||||
from nova import test
|
||||
|
||||
@@ -70,11 +70,15 @@ class S3APITestCase(test.TestCase):
|
||||
os.mkdir(FLAGS.buckets_path)
|
||||
|
||||
router = s3server.S3Application(FLAGS.buckets_path)
|
||||
server = wsgi.Server()
|
||||
server.start(router, FLAGS.s3_port, host=FLAGS.s3_host)
|
||||
self.server = wsgi.Server("S3 Objectstore",
|
||||
router,
|
||||
host=FLAGS.s3_host,
|
||||
port=FLAGS.s3_port)
|
||||
self.server.start()
|
||||
|
||||
if not boto.config.has_section('Boto'):
|
||||
boto.config.add_section('Boto')
|
||||
|
||||
boto.config.set('Boto', 'num_retries', '0')
|
||||
conn = s3.S3Connection(aws_access_key_id=self.admin_user.access,
|
||||
aws_secret_access_key=self.admin_user.secret,
|
||||
@@ -145,4 +149,5 @@ class S3APITestCase(test.TestCase):
|
||||
"""Tear down auth and test server."""
|
||||
self.auth_manager.delete_user('admin')
|
||||
self.auth_manager.delete_project('admin')
|
||||
self.server.stop()
|
||||
super(S3APITestCase, self).tearDown()
|
||||
|
||||
@@ -30,6 +30,7 @@ from nova import rpc
|
||||
from nova import test
|
||||
from nova import service
|
||||
from nova import manager
|
||||
from nova import wsgi
|
||||
from nova.compute import manager as compute_manager
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -349,3 +350,32 @@ class ServiceTestCase(test.TestCase):
|
||||
serv.stop()
|
||||
|
||||
db.service_destroy(ctxt, service_ref['id'])
|
||||
|
||||
|
||||
class TestWSGIService(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestWSGIService, self).setUp()
|
||||
self.stubs.Set(wsgi.Loader, "load_app", mox.MockAnything())
|
||||
|
||||
def test_service_random_port(self):
|
||||
test_service = service.WSGIService("test_service")
|
||||
self.assertEquals(0, test_service.port)
|
||||
test_service.start()
|
||||
self.assertNotEqual(0, test_service.port)
|
||||
test_service.stop()
|
||||
|
||||
|
||||
class TestLauncher(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestLauncher, self).setUp()
|
||||
self.stubs.Set(wsgi.Loader, "load_app", mox.MockAnything())
|
||||
self.service = service.WSGIService("test_service")
|
||||
|
||||
def test_launch_app(self):
|
||||
self.assertEquals(0, self.service.port)
|
||||
launcher = service.Launcher()
|
||||
launcher.launch_service(self.service)
|
||||
self.assertEquals(0, self.service.port)
|
||||
launcher.stop()
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"""
|
||||
Unit Tests for vlan network code
|
||||
"""
|
||||
import IPy
|
||||
import netaddr
|
||||
import os
|
||||
|
||||
from nova import context
|
||||
@@ -44,8 +44,8 @@ class VlanNetworkTestCase(base.NetworkTestCase):
|
||||
# TODO(vish): better way of adding floating ips
|
||||
self.context._project = self.projects[0]
|
||||
self.context.project_id = self.projects[0].id
|
||||
pubnet = IPy.IP(flags.FLAGS.floating_range)
|
||||
address = str(pubnet[0])
|
||||
pubnet = netaddr.IPNetwork(flags.FLAGS.floating_range)
|
||||
address = str(list(pubnet)[0])
|
||||
try:
|
||||
db.floating_ip_get_by_address(context.get_admin_context(), address)
|
||||
except exception.NotFound:
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
"""Unit tests for `nova.wsgi`."""
|
||||
|
||||
import os.path
|
||||
import tempfile
|
||||
|
||||
import unittest
|
||||
|
||||
import nova.exception
|
||||
import nova.test
|
||||
import nova.wsgi
|
||||
|
||||
|
||||
class TestLoaderNothingExists(unittest.TestCase):
|
||||
"""Loader tests where os.path.exists always returns False."""
|
||||
|
||||
def setUp(self):
|
||||
self._os_path_exists = os.path.exists
|
||||
os.path.exists = lambda _: False
|
||||
|
||||
def test_config_not_found(self):
|
||||
self.assertRaises(
|
||||
nova.exception.PasteConfigNotFound,
|
||||
nova.wsgi.Loader,
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
os.path.exists = self._os_path_exists
|
||||
|
||||
|
||||
class TestLoaderNormalFilesystem(unittest.TestCase):
|
||||
"""Loader tests with normal filesystem (unmodified os.path module)."""
|
||||
|
||||
_paste_config = """
|
||||
[app:test_app]
|
||||
use = egg:Paste#static
|
||||
document_root = /tmp
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.config = tempfile.NamedTemporaryFile(mode="w+t")
|
||||
self.config.write(self._paste_config.lstrip())
|
||||
self.config.seek(0)
|
||||
self.config.flush()
|
||||
self.loader = nova.wsgi.Loader(self.config.name)
|
||||
|
||||
def test_config_found(self):
|
||||
self.assertEquals(self.config.name, self.loader.config_path)
|
||||
|
||||
def test_app_not_found(self):
|
||||
self.assertRaises(
|
||||
nova.exception.PasteAppNotFound,
|
||||
self.loader.load_app,
|
||||
"non-existant app",
|
||||
)
|
||||
|
||||
def test_app_found(self):
|
||||
url_parser = self.loader.load_app("test_app")
|
||||
self.assertEquals("/tmp", url_parser.directory)
|
||||
|
||||
def tearDown(self):
|
||||
self.config.close()
|
||||
|
||||
|
||||
class TestWSGIServer(unittest.TestCase):
|
||||
"""WSGI server tests."""
|
||||
|
||||
def test_no_app(self):
|
||||
server = nova.wsgi.Server("test_app", None)
|
||||
self.assertEquals("test_app", server.name)
|
||||
|
||||
def test_start_random_port(self):
|
||||
server = nova.wsgi.Server("test_random_port", None, host="127.0.0.1")
|
||||
self.assertEqual(0, server.port)
|
||||
server.start()
|
||||
self.assertNotEqual(0, server.port)
|
||||
server.stop()
|
||||
server.wait()
|
||||
+69
-4
@@ -46,6 +46,7 @@ from eventlet.green import subprocess
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import version
|
||||
|
||||
|
||||
LOG = logging.getLogger("nova.utils")
|
||||
@@ -226,8 +227,10 @@ def novadir():
|
||||
return os.path.abspath(nova.__file__).split('nova/__init__.pyc')[0]
|
||||
|
||||
|
||||
def default_flagfile(filename='nova.conf'):
|
||||
for arg in sys.argv:
|
||||
def default_flagfile(filename='nova.conf', args=None):
|
||||
if args is None:
|
||||
args = sys.argv
|
||||
for arg in args:
|
||||
if arg.find('flagfile') != -1:
|
||||
break
|
||||
else:
|
||||
@@ -239,8 +242,8 @@ def default_flagfile(filename='nova.conf'):
|
||||
filename = "./nova.conf"
|
||||
if not os.path.exists(filename):
|
||||
filename = '/etc/nova/nova.conf'
|
||||
flagfile = ['--flagfile=%s' % filename]
|
||||
sys.argv = sys.argv[:1] + flagfile + sys.argv[1:]
|
||||
flagfile = '--flagfile=%s' % filename
|
||||
args.insert(1, flagfile)
|
||||
|
||||
|
||||
def debug(arg):
|
||||
@@ -279,6 +282,22 @@ EASIER_PASSWORD_SYMBOLS = ('23456789' # Removed: 0, 1
|
||||
'ABCDEFGHJKLMNPQRSTUVWXYZ') # Removed: I, O
|
||||
|
||||
|
||||
def usage_from_instance(instance_ref, **kw):
|
||||
usage_info = dict(
|
||||
tenant_id=instance_ref['project_id'],
|
||||
user_id=instance_ref['user_id'],
|
||||
instance_id=instance_ref['id'],
|
||||
instance_type=instance_ref['instance_type']['name'],
|
||||
instance_type_id=instance_ref['instance_type_id'],
|
||||
display_name=instance_ref['display_name'],
|
||||
created_at=str(instance_ref['created_at']),
|
||||
launched_at=str(instance_ref['launched_at']) \
|
||||
if instance_ref['launched_at'] else '',
|
||||
image_ref=instance_ref['image_ref'])
|
||||
usage_info.update(kw)
|
||||
return usage_info
|
||||
|
||||
|
||||
def generate_password(length=20, symbols=DEFAULT_PASSWORD_SYMBOLS):
|
||||
"""Generate a random password from the supplied symbols.
|
||||
|
||||
@@ -526,6 +545,16 @@ def loads(s):
|
||||
return json.loads(s)
|
||||
|
||||
|
||||
try:
|
||||
import anyjson
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
anyjson._modules.append(("nova.utils", "dumps", TypeError,
|
||||
"loads", ValueError))
|
||||
anyjson.force_implementation("nova.utils")
|
||||
|
||||
|
||||
_semaphores = {}
|
||||
|
||||
|
||||
@@ -741,3 +770,39 @@ def is_uuid_like(val):
|
||||
if not isinstance(val, basestring):
|
||||
return False
|
||||
return (len(val) == 36) and (val.count('-') == 4)
|
||||
|
||||
|
||||
class Bootstrapper(object):
|
||||
"""Provides environment bootstrapping capabilities for entry points."""
|
||||
|
||||
@staticmethod
|
||||
def bootstrap_binary(argv):
|
||||
"""Initialize the Nova environment using command line arguments."""
|
||||
Bootstrapper.setup_flags(argv)
|
||||
Bootstrapper.setup_logging()
|
||||
Bootstrapper.log_flags()
|
||||
|
||||
@staticmethod
|
||||
def setup_logging():
|
||||
"""Initialize logging and log a message indicating the Nova version."""
|
||||
logging.setup()
|
||||
logging.audit(_("Nova Version (%s)") %
|
||||
version.version_string_with_vcs())
|
||||
|
||||
@staticmethod
|
||||
def setup_flags(input_flags):
|
||||
"""Initialize flags, load flag file, and print help if needed."""
|
||||
default_flagfile(args=input_flags)
|
||||
FLAGS(input_flags or [])
|
||||
flags.DEFINE_flag(flags.HelpFlag())
|
||||
flags.DEFINE_flag(flags.HelpshortFlag())
|
||||
flags.DEFINE_flag(flags.HelpXMLFlag())
|
||||
FLAGS.ParseNewFlags()
|
||||
|
||||
@staticmethod
|
||||
def log_flags():
|
||||
"""Log the list of all active flags being used."""
|
||||
logging.audit(_("Currently active flags:"))
|
||||
for key in FLAGS:
|
||||
value = FLAGS.get(key, None)
|
||||
logging.audit(_("%(key)s : %(value)s" % locals()))
|
||||
|
||||
@@ -38,6 +38,7 @@ Supports KVM, LXC, QEMU, UML, and XEN.
|
||||
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import netaddr
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
@@ -53,8 +54,6 @@ from xml.etree import ElementTree
|
||||
from eventlet import greenthread
|
||||
from eventlet import tpool
|
||||
|
||||
import IPy
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
@@ -186,6 +185,7 @@ class LibvirtConnection(driver.ComputeDriver):
|
||||
|
||||
if state != power_state.RUNNING:
|
||||
continue
|
||||
self.firewall_driver.setup_basic_filtering(instance)
|
||||
self.firewall_driver.prepare_instance_filter(instance)
|
||||
self.firewall_driver.apply_instance_filter(instance)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"""Network-releated utilities for supporting libvirt connection code."""
|
||||
|
||||
|
||||
import IPy
|
||||
import netaddr
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
@@ -34,18 +34,18 @@ FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
def get_net_and_mask(cidr):
|
||||
net = IPy.IP(cidr)
|
||||
return str(net.net()), str(net.netmask())
|
||||
net = netaddr.IPNetwork(cidr)
|
||||
return str(net.ip), str(net.netmask)
|
||||
|
||||
|
||||
def get_net_and_prefixlen(cidr):
|
||||
net = IPy.IP(cidr)
|
||||
return str(net.net()), str(net.prefixlen())
|
||||
net = netaddr.IPNetwork(cidr)
|
||||
return str(net.ip), str(net._prefixlen)
|
||||
|
||||
|
||||
def get_ip_version(cidr):
|
||||
net = IPy.IP(cidr)
|
||||
return int(net.version())
|
||||
net = netaddr.IPNetwork(cidr)
|
||||
return int(net.version)
|
||||
|
||||
|
||||
def get_network_info(instance):
|
||||
|
||||
+112
-79
@@ -21,16 +21,16 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from xml.dom import minidom
|
||||
|
||||
import eventlet
|
||||
import eventlet.wsgi
|
||||
eventlet.patcher.monkey_patch(all=False, socket=True, time=True)
|
||||
import routes
|
||||
import greenlet
|
||||
import routes.middleware
|
||||
import webob
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from paste import deploy
|
||||
|
||||
from nova import exception
|
||||
@@ -39,49 +39,86 @@ from nova import log as logging
|
||||
from nova import utils
|
||||
|
||||
|
||||
eventlet.patcher.monkey_patch(socket=True, time=True)
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger('nova.wsgi')
|
||||
|
||||
|
||||
class WritableLogger(object):
|
||||
"""A thin wrapper that responds to `write` and logs."""
|
||||
|
||||
def __init__(self, logger, level=logging.DEBUG):
|
||||
self.logger = logger
|
||||
self.level = level
|
||||
|
||||
def write(self, msg):
|
||||
self.logger.log(self.level, msg)
|
||||
|
||||
|
||||
class Server(object):
|
||||
"""Server class to manage multiple WSGI sockets and applications."""
|
||||
"""Server class to manage a WSGI server, serving a WSGI application."""
|
||||
|
||||
def __init__(self, threads=1000):
|
||||
self.pool = eventlet.GreenPool(threads)
|
||||
self.socket_info = {}
|
||||
default_pool_size = 1000
|
||||
|
||||
def start(self, application, port, host='0.0.0.0', key=None, backlog=128):
|
||||
"""Run a WSGI server with the given application."""
|
||||
arg0 = sys.argv[0]
|
||||
logging.audit(_('Starting %(arg0)s on %(host)s:%(port)s') % locals())
|
||||
socket = eventlet.listen((host, port), backlog=backlog)
|
||||
self.pool.spawn_n(self._run, application, socket)
|
||||
if key:
|
||||
self.socket_info[key] = socket.getsockname()
|
||||
def __init__(self, name, app, host=None, port=None, pool_size=None):
|
||||
"""Initialize, but do not start, a WSGI server.
|
||||
|
||||
:param name: Pretty name for logging.
|
||||
:param app: The WSGI application to serve.
|
||||
:param host: IP address to serve the application.
|
||||
:param port: Port number to server the application.
|
||||
:param pool_size: Maximum number of eventlets to spawn concurrently.
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
self.name = name
|
||||
self.app = app
|
||||
self.host = host or "0.0.0.0"
|
||||
self.port = port or 0
|
||||
self._server = None
|
||||
self._socket = None
|
||||
self._pool = eventlet.GreenPool(pool_size or self.default_pool_size)
|
||||
self._logger = logging.getLogger("eventlet.wsgi.server")
|
||||
self._wsgi_logger = logging.WritableLogger(self._logger)
|
||||
|
||||
def _start(self):
|
||||
"""Run the blocking eventlet WSGI server.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
eventlet.wsgi.server(self._socket,
|
||||
self.app,
|
||||
custom_pool=self._pool,
|
||||
log=self._wsgi_logger)
|
||||
|
||||
def start(self, backlog=128):
|
||||
"""Start serving a WSGI application.
|
||||
|
||||
:param backlog: Maximum number of queued connections.
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
self._socket = eventlet.listen((self.host, self.port), backlog=backlog)
|
||||
self._server = eventlet.spawn(self._start)
|
||||
(self.host, self.port) = self._socket.getsockname()
|
||||
LOG.info(_("Started %(name)s on %(host)s:%(port)s") % self.__dict__)
|
||||
|
||||
def stop(self):
|
||||
"""Stop this server.
|
||||
|
||||
This is not a very nice action, as currently the method by which a
|
||||
server is stopped is by killing it's eventlet.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
LOG.info(_("Stopping WSGI server."))
|
||||
self._server.kill()
|
||||
|
||||
def wait(self):
|
||||
"""Wait until all servers have completed running."""
|
||||
try:
|
||||
self.pool.waitall()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
"""Block, until the server has stopped.
|
||||
|
||||
def _run(self, application, socket):
|
||||
"""Start a WSGI server in a new green thread."""
|
||||
logger = logging.getLogger('eventlet.wsgi.server')
|
||||
eventlet.wsgi.server(socket, application, custom_pool=self.pool,
|
||||
log=WritableLogger(logger))
|
||||
Waits on the server's eventlet to finish, then returns.
|
||||
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
try:
|
||||
self._server.wait()
|
||||
except greenlet.GreenletExit:
|
||||
LOG.info(_("WSGI server has stopped."))
|
||||
|
||||
|
||||
class Request(webob.Request):
|
||||
@@ -309,55 +346,51 @@ class Router(object):
|
||||
return app
|
||||
|
||||
|
||||
def paste_config_file(basename):
|
||||
"""Find the best location in the system for a paste config file.
|
||||
class Loader(object):
|
||||
"""Used to load WSGI applications from paste configurations."""
|
||||
|
||||
Search Order
|
||||
------------
|
||||
def __init__(self, config_path=None):
|
||||
"""Initialize the loader, and attempt to find the config.
|
||||
|
||||
The search for a paste config file honors `FLAGS.state_path`, which in a
|
||||
version checked out from bzr will be the `nova` directory in the top level
|
||||
of the checkout, and in an installation for a package for your distribution
|
||||
will likely point to someplace like /etc/nova.
|
||||
:param config_path: Full or relative path to the paste config.
|
||||
:returns: None
|
||||
|
||||
This method tries to load places likely to be used in development or
|
||||
experimentation before falling back to the system-wide configuration
|
||||
in `/etc/nova/`.
|
||||
"""
|
||||
config_path = config_path or FLAGS.api_paste_config
|
||||
self.config_path = self._find_config(config_path)
|
||||
|
||||
* Current working directory
|
||||
* the `etc` directory under state_path, because when working on a checkout
|
||||
from bzr this will point to the default
|
||||
* top level of FLAGS.state_path, for distributions
|
||||
* /etc/nova, which may not be diffrerent from state_path on your distro
|
||||
def _find_config(self, config_path):
|
||||
"""Find the paste configuration file using the given hint.
|
||||
|
||||
"""
|
||||
configfiles = [basename,
|
||||
os.path.join(FLAGS.state_path, 'etc', 'nova', basename),
|
||||
os.path.join(FLAGS.state_path, 'etc', basename),
|
||||
os.path.join(FLAGS.state_path, basename),
|
||||
'/etc/nova/%s' % basename]
|
||||
for configfile in configfiles:
|
||||
if os.path.exists(configfile):
|
||||
return configfile
|
||||
:param config_path: Full or relative path to the paste config.
|
||||
:returns: Full path of the paste config, if it exists.
|
||||
:raises: `nova.exception.PasteConfigNotFound`
|
||||
|
||||
"""
|
||||
possible_locations = [
|
||||
config_path,
|
||||
os.path.join(FLAGS.state_path, "etc", "nova", config_path),
|
||||
os.path.join(FLAGS.state_path, "etc", config_path),
|
||||
os.path.join(FLAGS.state_path, config_path),
|
||||
"/etc/nova/%s" % config_path,
|
||||
]
|
||||
|
||||
def load_paste_configuration(filename, appname):
|
||||
"""Returns a paste configuration dict, or None."""
|
||||
filename = os.path.abspath(filename)
|
||||
config = None
|
||||
try:
|
||||
config = deploy.appconfig('config:%s' % filename, name=appname)
|
||||
except LookupError:
|
||||
pass
|
||||
return config
|
||||
for path in possible_locations:
|
||||
if os.path.exists(path):
|
||||
return os.path.abspath(path)
|
||||
|
||||
raise exception.PasteConfigNotFound(path=os.path.abspath(config_path))
|
||||
|
||||
def load_paste_app(filename, appname):
|
||||
"""Builds a wsgi app from a paste config, None if app not configured."""
|
||||
filename = os.path.abspath(filename)
|
||||
app = None
|
||||
try:
|
||||
app = deploy.loadapp('config:%s' % filename, name=appname)
|
||||
except LookupError:
|
||||
pass
|
||||
return app
|
||||
def load_app(self, name):
|
||||
"""Return the paste URLMap wrapped WSGI application.
|
||||
|
||||
:param name: Name of the application to load.
|
||||
:returns: Paste URLMap object wrapping the requested application.
|
||||
:raises: `nova.exception.PasteAppNotFound`
|
||||
|
||||
"""
|
||||
try:
|
||||
return deploy.loadapp("config:%s" % self.config_path, name=name)
|
||||
except LookupError as err:
|
||||
LOG.error(err)
|
||||
raise exception.PasteAppNotFound(name=name, path=self.config_path)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -224,9 +225,11 @@
|
||||
@@ -224,6 +225,7 @@
|
||||
|
||||
remove)
|
||||
if [ "${TYPE}" = "vif" ] ;then
|
||||
@@ -16,7 +16,3 @@
|
||||
xenstore-rm "${HOTPLUG}/hotplug"
|
||||
fi
|
||||
logger -t scripts-vif "${dev} has been removed"
|
||||
remove_from_bridge
|
||||
;;
|
||||
esac
|
||||
+
|
||||
|
||||
+3
-4
@@ -69,7 +69,6 @@ from nose import core
|
||||
from nose import result
|
||||
|
||||
from nova import log as logging
|
||||
from nova.tests import fake_flags
|
||||
|
||||
|
||||
class _AnsiColorizer(object):
|
||||
@@ -211,11 +210,11 @@ class NovaTestResult(result.TextTestResult):
|
||||
break
|
||||
sys.stdout = stdout
|
||||
|
||||
# NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate
|
||||
# error results in it failing to be initialized later. Otherwise,
|
||||
# NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate
|
||||
# error results in it failing to be initialized later. Otherwise,
|
||||
# _handleElapsedTime will fail, causing the wrong error message to
|
||||
# be outputted.
|
||||
self.start_time = time.time()
|
||||
self.start_time = time.time()
|
||||
|
||||
def getDescription(self, test):
|
||||
return str(test)
|
||||
|
||||
@@ -6,6 +6,7 @@ function usage {
|
||||
echo ""
|
||||
echo " -V, --virtual-env Always use virtualenv. Install automatically if not present"
|
||||
echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment"
|
||||
echo " -r, --recreate-db Recreate the test database."
|
||||
echo " -x, --stop Stop running tests after the first error or failure."
|
||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||
echo " -p, --pep8 Just run pep8"
|
||||
@@ -23,6 +24,7 @@ function process_option {
|
||||
-h|--help) usage;;
|
||||
-V|--virtual-env) let always_venv=1; let never_venv=0;;
|
||||
-N|--no-virtual-env) let always_venv=0; let never_venv=1;;
|
||||
-r|--recreate-db) let recreate_db=1;;
|
||||
-f|--force) let force=1;;
|
||||
-p|--pep8) let just_pep8=1;;
|
||||
-*) noseopts="$noseopts $1";;
|
||||
@@ -39,6 +41,7 @@ noseargs=
|
||||
noseopts=
|
||||
wrapper=""
|
||||
just_pep8=0
|
||||
recreate_db=0
|
||||
|
||||
for arg in "$@"; do
|
||||
process_option $arg
|
||||
@@ -108,6 +111,10 @@ if [ $just_pep8 -eq 1 ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ $recreate_db -eq 1 ]; then
|
||||
rm tests.sqlite
|
||||
fi
|
||||
|
||||
run_tests || exit
|
||||
|
||||
# NOTE(sirp): we only want to run pep8 when we're running the full-test suite,
|
||||
|
||||
+2
-3
@@ -1,14 +1,13 @@
|
||||
SQLAlchemy==0.6.3
|
||||
pep8==0.5.0
|
||||
pep8==0.6.1
|
||||
pylint==0.19
|
||||
IPy==0.70
|
||||
Cheetah==2.4.4
|
||||
M2Crypto==0.20.2
|
||||
amqplib==0.6.1
|
||||
anyjson==0.2.4
|
||||
boto==1.9b
|
||||
carrot==0.10.5
|
||||
eventlet==0.9.12
|
||||
eventlet
|
||||
lockfile==0.8
|
||||
python-novaclient==2.5.3
|
||||
python-daemon==1.5.5
|
||||
|
||||
Reference in New Issue
Block a user