Merge trunk + wsgirouter

This commit is contained in:
Todd Willey 2011-01-12 17:47:43 -05:00
commit f58b4f74e3
15 changed files with 140 additions and 80 deletions

View File

@ -21,6 +21,7 @@ Jesse Andrews <anotherjesse@gmail.com>
Joe Heck <heckj@mac.com> Joe Heck <heckj@mac.com>
Joel Moore <joelbm24@gmail.com> Joel Moore <joelbm24@gmail.com>
Jonathan Bryce <jbryce@jbryce.com> Jonathan Bryce <jbryce@jbryce.com>
Josh Durgin <joshd@hq.newdream.net>
Josh Kearney <josh.kearney@rackspace.com> Josh Kearney <josh.kearney@rackspace.com>
Joshua McKenty <jmckenty@gmail.com> Joshua McKenty <jmckenty@gmail.com>
Justin Santa Barbara <justin@fathomdb.com> Justin Santa Barbara <justin@fathomdb.com>

View File

@ -40,58 +40,47 @@ from nova import flags
from nova import log as logging from nova import log as logging
from nova import wsgi from nova import wsgi
logging.basicConfig()
LOG = logging.getLogger('nova.api') LOG = logging.getLogger('nova.api')
LOG.setLevel(logging.DEBUG)
LOG.addHandler(logging.StreamHandler())
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
API_ENDPOINTS = ['ec2', 'openstack'] API_ENDPOINTS = ['ec2', 'osapi']
def load_configuration(paste_config): def load_configuration(paste_config, name):
"""Load the paste configuration from the config file and return it.""" """Load the paste configuration from the config file and return it."""
config = None config = None
# Try each known name to get the global DEFAULTS, which will give ports
for name in API_ENDPOINTS:
try: try:
config = deploy.appconfig("config:%s" % paste_config, name=name) config = deploy.appconfig("config:%s" % paste_config, name=name)
except LookupError:
pass
if config:
verbose = config.get('verbose', None)
if verbose:
FLAGS.verbose = int(verbose) == 1
if FLAGS.verbose:
logging.getLogger().setLevel(logging.DEBUG)
return config return config
LOG.debug(_("Paste config at %s has no secion for known apis"), except LookupError:
paste_config) return None
print _("Paste config at %s has no secion for any known apis") % \
paste_config
os.exit(1)
def launch_api(paste_config_file, section, server, port, host):
"""Launch an api server from the specified port and IP."""
LOG.debug(_("Launching %s api on %s:%s"), section, host, port)
app = deploy.loadapp('config:%s' % paste_config_file, name=section)
server.start(app, int(port), host)
def run_app(paste_config_file): def run_app(paste_config_file):
LOG.debug(_("Using paste.deploy config at: %s"), configfile) LOG.debug(_("Using paste.deploy config at: %s"), configfile)
config = load_configuration(paste_config_file)
LOG.debug(_("Configuration: %r"), config)
server = wsgi.Server() server = wsgi.Server()
ip = config.get('host', '0.0.0.0') apps = []
for api in API_ENDPOINTS: for api in API_ENDPOINTS:
port = config.get("%s_port" % api, None) config = load_configuration(paste_config_file, api)
if not port: if config is None:
continue continue
host = config.get("%s_host" % api, ip) if int(config.get('verbose', 0)) == 1:
launch_api(configfile, api, server, port, host) FLAGS.verbose = True
LOG.debug(_("All api servers launched, now waiting")) host = config.get("%s_host" % api, config.get('host', '0.0.0.0'))
port = config.get("%s_port" % api, getattr(FLAGS, "%s_port" % api))
setattr(FLAGS, "%s_host" % api, host)
setattr(FLAGS, "%s_port" % api, port)
LOG.info(_("Running %s API"), api)
app = deploy.loadapp('config:%s' % paste_config_file, name=api)
apps.append((app, int(port), host))
if len(apps) == 0:
LOG.error(_("No known API applications configured in %s."),
paste_config_file)
else:
for app in apps:
server.start(*app)
server.wait() server.wait()

View File

@ -44,7 +44,6 @@ from nova import wsgi
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
flags.DEFINE_string('osapi_host', '0.0.0.0', 'OpenStack API host') flags.DEFINE_string('osapi_host', '0.0.0.0', 'OpenStack API host')
flags.DEFINE_integer('ec2api_port', 8773, 'EC2 API port') flags.DEFINE_integer('ec2api_port', 8773, 'EC2 API port')
flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host') flags.DEFINE_string('ec2api_host', '0.0.0.0', 'EC2 API host')

View File

@ -77,12 +77,14 @@ from nova import crypto
from nova import db from nova import db
from nova import exception from nova import exception
from nova import flags from nova import flags
from nova import log as logging
from nova import quota from nova import quota
from nova import utils from nova import utils
from nova.auth import manager from nova.auth import manager
from nova.cloudpipe import pipelib from nova.cloudpipe import pipelib
logging.basicConfig()
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DECLARE('fixed_range', 'nova.network.manager') flags.DECLARE('fixed_range', 'nova.network.manager')
flags.DECLARE('num_networks', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager')

View File

@ -1,9 +1,5 @@
[DEFAULT] [DEFAULT]
verbose = 1 verbose = 1
ec2_port = 8773
ec2_address = 0.0.0.0
openstack_port = 8774
openstack_address = 0.0.0.0
####### #######
# EC2 # # EC2 #
@ -64,13 +60,13 @@ paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.f
# Openstack # # Openstack #
############# #############
[composite:openstack] [composite:osapi]
use = egg:Paste#urlmap use = egg:Paste#urlmap
/: osversions /: osversions
/v1.0: openstackapi /v1.0: openstackapi
[pipeline:openstackapi] [pipeline:openstackapi]
pipeline = faultwrap auth ratelimit osapi pipeline = faultwrap auth ratelimit osapiapp
[filter:faultwrap] [filter:faultwrap]
paste.filter_factory = nova.api.openstack:FaultWrapper.factory paste.filter_factory = nova.api.openstack:FaultWrapper.factory
@ -81,7 +77,7 @@ paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory
[filter:ratelimit] [filter:ratelimit]
paste.filter_factory = nova.api.openstack.ratelimiting:RateLimitingMiddleware.factory paste.filter_factory = nova.api.openstack.ratelimiting:RateLimitingMiddleware.factory
[app:osapi] [app:osapiapp]
paste.app_factory = nova.api.openstack:APIRouter.factory paste.app_factory = nova.api.openstack:APIRouter.factory
[pipeline:osversions] [pipeline:osversions]

View File

@ -278,8 +278,7 @@ class Authorizer(wsgi.Middleware):
return self.application return self.application
else: else:
LOG.audit(_("Unauthorized request for controller=%s " LOG.audit(_("Unauthorized request for controller=%s "
"and action=%s"), controller_name, action, "and action=%s"), controller, action, context=context)
context=context)
raise webob.exc.HTTPUnauthorized() raise webob.exc.HTTPUnauthorized()
def _matches_any_role(self, context, roles): def _matches_any_role(self, context, roles):

View File

@ -263,15 +263,15 @@ class CloudController(object):
name, _sep, host = region.partition('=') name, _sep, host = region.partition('=')
endpoint = '%s://%s:%s%s' % (FLAGS.ec2_prefix, endpoint = '%s://%s:%s%s' % (FLAGS.ec2_prefix,
host, host,
FLAGS.cc_port, FLAGS.ec2_port,
FLAGS.ec2_suffix) FLAGS.ec2_suffix)
regions.append({'regionName': name, regions.append({'regionName': name,
'regionEndpoint': endpoint}) 'regionEndpoint': endpoint})
else: else:
regions = [{'regionName': 'nova', regions = [{'regionName': 'nova',
'regionEndpoint': '%s://%s:%s%s' % (FLAGS.ec2_prefix, 'regionEndpoint': '%s://%s:%s%s' % (FLAGS.ec2_prefix,
FLAGS.cc_host, FLAGS.ec2_host,
FLAGS.cc_port, FLAGS.ec2_port,
FLAGS.ec2_suffix)}] FLAGS.ec2_suffix)}]
return {'regionInfo': regions} return {'regionInfo': regions}

View File

@ -682,7 +682,7 @@ class AuthManager(object):
region, _sep, region_host = item.partition("=") region, _sep, region_host = item.partition("=")
regions[region] = region_host regions[region] = region_host
else: else:
regions = {'nova': FLAGS.cc_host} regions = {'nova': FLAGS.ec2_host}
for region, host in regions.iteritems(): for region, host in regions.iteritems():
rc = self.__generate_rc(user, rc = self.__generate_rc(user,
pid, pid,
@ -727,28 +727,28 @@ class AuthManager(object):
def __generate_rc(user, pid, use_dmz=True, host=None): def __generate_rc(user, pid, use_dmz=True, host=None):
"""Generate rc file for user""" """Generate rc file for user"""
if use_dmz: if use_dmz:
cc_host = FLAGS.cc_dmz ec2_host = FLAGS.ec2_dmz_host
else: else:
cc_host = FLAGS.cc_host ec2_host = FLAGS.ec2_host
# NOTE(vish): Always use the dmz since it is used from inside the # NOTE(vish): Always use the dmz since it is used from inside the
# instance # instance
s3_host = FLAGS.s3_dmz s3_host = FLAGS.s3_dmz
if host: if host:
s3_host = host s3_host = host
cc_host = host ec2_host = host
rc = open(FLAGS.credentials_template).read() rc = open(FLAGS.credentials_template).read()
rc = rc % {'access': user.access, rc = rc % {'access': user.access,
'project': pid, 'project': pid,
'secret': user.secret, 'secret': user.secret,
'ec2': '%s://%s:%s%s' % (FLAGS.ec2_prefix, 'ec2': '%s://%s:%s%s' % (FLAGS.ec2_scheme,
cc_host, ec2_host,
FLAGS.cc_port, FLAGS.ec2_port,
FLAGS.ec2_suffix), FLAGS.ec2_path),
's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port), 's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
'os': '%s://%s:%s%s' % (FLAGS.os_prefix, 'os': '%s://%s:%s%s' % (FLAGS.osapi_scheme,
cc_host, ec2_host,
FLAGS.cc_port, FLAGS.osapi_port,
FLAGS.os_suffix), FLAGS.osapi_path),
'user': user.name, 'user': user.name,
'nova': FLAGS.ca_file, 'nova': FLAGS.ca_file,
'cert': FLAGS.credential_cert_file, 'cert': FLAGS.credential_cert_file,

View File

@ -68,8 +68,8 @@ class CloudPipe(object):
shellfile = open(FLAGS.boot_script_template, "r") shellfile = open(FLAGS.boot_script_template, "r")
s = string.Template(shellfile.read()) s = string.Template(shellfile.read())
shellfile.close() shellfile.close()
boot_script = s.substitute(cc_dmz=FLAGS.cc_dmz, boot_script = s.substitute(cc_dmz=FLAGS.ec2_dmz_host,
cc_port=FLAGS.cc_port, cc_port=FLAGS.ec2_port,
dmz_net=FLAGS.dmz_net, dmz_net=FLAGS.dmz_net,
dmz_mask=FLAGS.dmz_mask, dmz_mask=FLAGS.dmz_mask,
num_vpn=FLAGS.cnt_vpn_clients) num_vpn=FLAGS.cnt_vpn_clients)

View File

@ -765,12 +765,14 @@ def instance_get_by_id(context, instance_id):
if is_admin_context(context): if is_admin_context(context):
result = session.query(models.Instance).\ result = session.query(models.Instance).\
options(joinedload('security_groups')).\ options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.floating_ips')).\
filter_by(id=instance_id).\ filter_by(id=instance_id).\
filter_by(deleted=can_read_deleted(context)).\ filter_by(deleted=can_read_deleted(context)).\
first() first()
elif is_user_context(context): elif is_user_context(context):
result = session.query(models.Instance).\ result = session.query(models.Instance).\
options(joinedload('security_groups')).\ options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.floating_ips')).\
filter_by(project_id=context.project_id).\ filter_by(project_id=context.project_id).\
filter_by(id=instance_id).\ filter_by(id=instance_id).\
filter_by(deleted=False).\ filter_by(deleted=False).\

View File

@ -254,13 +254,15 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host')
DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval') DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval')
DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts') DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts')
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
DEFINE_string('ec2_prefix', 'http', 'prefix for ec2') DEFINE_string('ec2_host', '$my_ip', 'ip of api server')
DEFINE_string('os_prefix', 'http', 'prefix for openstack') DEFINE_string('ec2_dmz_host', '$my_ip', 'internal ip of api server')
DEFINE_string('cc_host', '$my_ip', 'ip of api server') DEFINE_integer('ec2_port', 8773, 'cloud controller port')
DEFINE_string('cc_dmz', '$my_ip', 'internal ip of api server') DEFINE_string('ec2_scheme', 'http', 'prefix for ec2')
DEFINE_integer('cc_port', 8773, 'cloud controller port') DEFINE_string('ec2_path', '/services/Cloud', 'suffix for ec2')
DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2') DEFINE_string('osapi_host', '$my_ip', 'ip of api server')
DEFINE_string('os_suffix', '/v1.0/', 'suffix for openstack') DEFINE_string('osapi_scheme', 'http', 'prefix for openstack')
DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
DEFINE_string('osapi_path', '/v1.0/', 'suffix for openstack')
DEFINE_string('default_project', 'openstack', 'default project for openstack') DEFINE_string('default_project', 'openstack', 'default project for openstack')
DEFINE_string('default_image', 'ami-11111', DEFINE_string('default_image', 'ami-11111',

View File

@ -60,7 +60,7 @@ def metadata_forward():
"""Create forwarding rule for metadata""" """Create forwarding rule for metadata"""
_confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 " _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 "
"-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT "
"--to-destination %s:%s" % (FLAGS.cc_dmz, FLAGS.cc_port)) "--to-destination %s:%s" % (FLAGS.ec2_dmz_host, FLAGS.ec2_port))
def init_host(): def init_host():

View File

@ -243,11 +243,24 @@ class LibvirtConnection(object):
def attach_volume(self, instance_name, device_path, mountpoint): def attach_volume(self, instance_name, device_path, mountpoint):
virt_dom = self._conn.lookupByName(instance_name) virt_dom = self._conn.lookupByName(instance_name)
mount_device = mountpoint.rpartition("/")[2] mount_device = mountpoint.rpartition("/")[2]
if device_path.startswith('/dev/'):
xml = """<disk type='block'> xml = """<disk type='block'>
<driver name='qemu' type='raw'/> <driver name='qemu' type='raw'/>
<source dev='%s'/> <source dev='%s'/>
<target dev='%s' bus='virtio'/> <target dev='%s' bus='virtio'/>
</disk>""" % (device_path, mount_device) </disk>""" % (device_path, mount_device)
elif ':' in device_path:
(protocol, name) = device_path.split(':')
xml = """<disk type='network'>
<driver name='qemu' type='raw'/>
<source protocol='%s' name='%s'/>
<target dev='%s' bus='virtio'/>
</disk>""" % (protocol,
name,
mount_device)
else:
raise exception.Invalid(_("Invalid device path %s") % device_path)
virt_dom.attachDevice(xml) virt_dom.attachDevice(xml)
def _get_disk_xml(self, xml, device): def _get_disk_xml(self, xml, device):

View File

@ -252,7 +252,7 @@ class VMOps(object):
raise Exception(_("suspend: instance not present %s") % raise Exception(_("suspend: instance not present %s") %
instance_name) instance_name)
task = self._session.call_xenapi('Async.VM.suspend', vm) task = self._session.call_xenapi('Async.VM.suspend', vm)
self._wait_with_callback(task, callback) self._wait_with_callback(instance.id, task, callback)
def resume(self, instance, callback): def resume(self, instance, callback):
"""resume the specified instance""" """resume the specified instance"""
@ -262,7 +262,7 @@ class VMOps(object):
raise Exception(_("resume: instance not present %s") % raise Exception(_("resume: instance not present %s") %
instance_name) instance_name)
task = self._session.call_xenapi('Async.VM.resume', vm, False, True) task = self._session.call_xenapi('Async.VM.resume', vm, False, True)
self._wait_with_callback(task, callback) self._wait_with_callback(instance.id, task, callback)
def get_info(self, instance_id): def get_info(self, instance_id):
"""Return data about VM instance""" """Return data about VM instance"""

View File

@ -49,6 +49,8 @@ flags.DEFINE_string('iscsi_target_prefix', 'iqn.2010-10.org.openstack:',
'prefix for iscsi volumes') 'prefix for iscsi volumes')
flags.DEFINE_string('iscsi_ip_prefix', '127.0', flags.DEFINE_string('iscsi_ip_prefix', '127.0',
'discover volumes on the ip that starts with this prefix') 'discover volumes on the ip that starts with this prefix')
flags.DEFINE_string('rbd_pool', 'rbd',
'the rbd pool in which volumes are stored')
class VolumeDriver(object): class VolumeDriver(object):
@ -314,3 +316,58 @@ class FakeISCSIDriver(ISCSIDriver):
"""Execute that simply logs the command.""" """Execute that simply logs the command."""
LOG.debug(_("FAKE ISCSI: %s"), cmd) LOG.debug(_("FAKE ISCSI: %s"), cmd)
return (None, None) return (None, None)
class RBDDriver(VolumeDriver):
"""Implements RADOS block device (RBD) volume commands"""
def check_for_setup_error(self):
"""Returns an error if prerequisites aren't met"""
(stdout, stderr) = self._execute("rados lspools")
pools = stdout.split("\n")
if not FLAGS.rbd_pool in pools:
raise exception.Error(_("rbd has no pool %s") %
FLAGS.rbd_pool)
def create_volume(self, volume):
"""Creates a logical volume."""
if int(volume['size']) == 0:
size = 100
else:
size = int(volume['size']) * 1024
self._try_execute("rbd --pool %s --size %d create %s" %
(FLAGS.rbd_pool,
size,
volume['name']))
def delete_volume(self, volume):
"""Deletes a logical volume."""
self._try_execute("rbd --pool %s rm %s" %
(FLAGS.rbd_pool,
volume['name']))
def local_path(self, volume):
"""Returns the path of the rbd volume."""
# This is the same as the remote path
# since qemu accesses it directly.
return self.discover_volume(volume)
def ensure_export(self, context, volume):
"""Synchronously recreates an export for a logical volume."""
pass
def create_export(self, context, volume):
"""Exports the volume"""
pass
def remove_export(self, context, volume):
"""Removes an export for a logical volume"""
pass
def discover_volume(self, volume):
"""Discover volume on a remote host"""
return "rbd:%s/%s" % (FLAGS.rbd_pool, volume['name'])
def undiscover_volume(self, volume):
"""Undiscover volume on a remote host"""
pass