From 089bdfa8c2f0f116b55c69bbcde6fca6632cb145 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月20日 15:20:23 -0600
Subject: [PATCH 001/109] should be writing some kindof network info to the
xenstore now, hopefully
---
nova/db/api.py | 5 +++++
nova/db/sqlalchemy/api.py | 6 ++++++
nova/db/sqlalchemy/models.py | 1 +
nova/virt/xenapi/vmops.py | 39 ++++++++++++++++++++++++++++--------
4 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/nova/db/api.py b/nova/db/api.py
index f9d561587b19..7b37bce2fbec 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -501,6 +501,11 @@ def network_get(context, network_id):
return IMPL.network_get(context, network_id)
+def network_get_all(context):
+ """Get all networks"""
+ returm IMPL.network_get_all(context)
+
+
# pylint: disable-msg=C0103
def network_get_associated_fixed_ips(context, network_id):
"""Get all network's ips that have been associated."""
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index b63b84bed595..053780158783 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1054,6 +1054,12 @@ def network_get(context, network_id, session=None):
return result
+@require_context
+def network_get_all(context):
+ session = get_session()
+ return session.query(models.Network).all()
+
+
# NOTE(vish): pylint complains because of the long method name, but
# it fits with the names of the rest of the methods
# pylint: disable-msg=C0103
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index c54ebe3ba588..dc476acf4118 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -369,6 +369,7 @@ class Network(BASE, NovaBase):
"vpn_public_port"),
{'mysql_engine': 'InnoDB'})
id = Column(Integer, primary_key=True)
+ label = Column(String(255))
injected = Column(Boolean, default=False)
cidr = Column(String(255), unique=True)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 6c2fd6a687af..882b9d9d603b 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -67,11 +67,6 @@ class VMOps(object):
raise exception.Duplicate(_('Attempted to create'
' non-unique name %s') % instance.name)
- bridge = db.network_get_by_instance(context.get_admin_context(),
- instance['id'])['bridge']
- network_ref = \
- NetworkHelper.find_network_with_bridge(self._session, bridge)
-
user = AuthManager().get_user(instance.user_id)
project = AuthManager().get_project(instance.project_id)
#if kernel is not present we must download a raw disk
@@ -99,9 +94,29 @@ class VMOps(object):
instance, kernel, ramdisk, pv_kernel)
VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True)
- if network_ref:
- VMHelper.create_vif(self._session, vm_ref,
- network_ref, instance.mac_address)
+ # write network info
+ network = db.network_get_by_instance(context.get_admin_context(),
+ instance['id'])
+ for network in db.network_get_all():
+ mapping = {'label': network['label'],
+ 'gateway': network['gateway'],
+ 'mac': instance.mac_address,
+ 'dns': network['dns'],
+ 'ips': [{'netmask': network['netmask'],
+ 'enabled': '1',
+ 'ip': 192.168.3.3}]} # <===== CHANGE!!!! + self.write_network_config_to_xenstore(vm_ref, mapping) + + bridge = network['bridge'] + network_ref = \ + NetworkHelper.find_network_with_bridge(self._session, bridge) + + if network_ref: + VMHelper.create_vif(self._session, vm_ref, + network_ref, instance.mac_address) + + # call reset networking + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) LOG.info(_('Spawning VM %s created %s.'), instance.name, vm_ref) @@ -341,6 +356,14 @@ class VMOps(object): # TODO: implement this! return 'http://fakeajaxconsole/fake_url' + def reset_networking(self, instance): + vm = self._get_vm_opaque_ref(instance) + self.write_to_xenstore(vm, "resetnetwork", "") + + def write_network_config_to_xenstore(self, instance): + vm = self._get_vm_opaque_ref(instance) + self.write_to_param_xenstore(vm, mapping) + def list_from_xenstore(self, vm, path): """Runs the xenstore-ls command to get a listing of all records from 'path' downward. Returns a dict with the sub-paths as keys, From 9b993d50835c79d23dca422335de362ebaf7f4fa Mon Sep 17 00:00:00 2001 From: Trey Morris
Date: 2011年1月20日 15:47:08 -0600
Subject: [PATCH 002/109] added plugin call for resetnetworking
---
nova/virt/xenapi/vmops.py | 3 ++-
plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 12 ++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 882b9d9d603b..7f9e78df516d 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -358,7 +358,8 @@ class VMOps(object):
def reset_networking(self, instance):
vm = self._get_vm_opaque_ref(instance)
- self.write_to_xenstore(vm, "resetnetwork", "")
+ args = {'id': str(uuid.uuid4())}
+ resp = self._make_agent_call('resetnetwork', vm, '', args)
def write_network_config_to_xenstore(self, instance):
vm = self._get_vm_opaque_ref(instance)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
index 12c3a19c8d0c..5c5ec7c4571f 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
@@ -91,6 +91,18 @@ def password(self, arg_dict):
return resp
+@jsonify
+def resetnetwork(self, arg_dict):
+ """
+ writes a resquest to xenstore that tells the agent to reset the networking
+
+ """
+ arg_dict['value'] = json.dumps({'name': 'resetnetwork', 'value': ''})
+ request_id = arg_dict['id']
+ arg_dict['path'] = "data/host/%s" % request_id
+ xenstore.write_record(self, arg_dict)
+
+
def _wait_for_agent(self, request_id, arg_dict):
"""Periodically checks xenstore for a response from the agent.
The request is always written to 'data/host/{id}', and
From 8d1798008fcec536f1117a275b168ca449f1dfbf Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月20日 16:19:07 -0600
Subject: [PATCH 003/109] syntax error
---
nova/db/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/db/api.py b/nova/db/api.py
index 7b37bce2fbec..f22cd561594a 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -503,7 +503,7 @@ def network_get(context, network_id):
def network_get_all(context):
"""Get all networks"""
- returm IMPL.network_get_all(context)
+ return IMPL.network_get_all(context)
# pylint: disable-msg=C0103
From f77043d44aa640e1811a3fe236fc8fd5dfecf990 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月20日 16:27:09 -0600
Subject: [PATCH 004/109] syntax
---
nova/virt/xenapi/vmops.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 7f9e78df516d..1045d5d9869b 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -104,7 +104,7 @@ class VMOps(object):
'dns': network['dns'],
'ips': [{'netmask': network['netmask'],
'enabled': '1',
- 'ip': 192.168.3.3}]} # <===== CHANGE!!!! + 'ip': '192.168.3.3'}]} # <===== CHANGE!!!! self.write_network_config_to_xenstore(vm_ref, mapping) bridge = network['bridge'] From f38196b0eb7a11501f9b0ffa9409c05510798761 Mon Sep 17 00:00:00 2001 From: Trey Morris
Date: 2011年1月20日 18:08:01 -0600
Subject: [PATCH 005/109] added default label to nova-manage and
create_networks
---
bin/nova-manage | 5 +++--
nova/network/manager.py | 8 +++++++-
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index d0901ddfcaac..38d36ab0f4a0 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -442,7 +442,7 @@ class NetworkCommands(object):
def create(self, fixed_range=None, num_networks=None,
network_size=None, vlan_start=None, vpn_start=None,
- fixed_range_v6=None):
+ fixed_range_v6=None, label='public'):
"""Creates fixed ips for host by range
arguments: [fixed_range=FLAG], [num_networks=FLAG],
[network_size=FLAG], [vlan_start=FLAG],
@@ -463,7 +463,8 @@ class NetworkCommands(object):
net_manager.create_networks(context.get_admin_context(),
fixed_range, int(num_networks),
int(network_size), int(vlan_start),
- int(vpn_start), fixed_range_v6)
+ int(vpn_start), fixed_range_v6,
+ label)
class ServiceCommands(object):
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 61de8055abfe..a377c40c6119 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -325,11 +325,12 @@ class FlatManager(NetworkManager):
pass
def create_networks(self, context, cidr, num_networks, network_size,
- cidr_v6, *args, **kwargs):
+ cidr_v6, label, *args, **kwargs):
"""Create networks based on parameters."""
fixed_net = IPy.IP(cidr)
fixed_net_v6 = IPy.IP(cidr_v6)
significant_bits_v6 = 64
+ count = 1
for index in range(num_networks):
start = index * network_size
significant_bits = 32 - int(math.log(network_size, 2))
@@ -342,6 +343,11 @@ class FlatManager(NetworkManager):
net['gateway'] = str(project_net[1])
net['broadcast'] = str(project_net.broadcast())
net['dhcp_start'] = str(project_net[2])
+ if num_networks> 1:
+ net['label'] = "%s_%d" % (label, count)
+ else:
+ net['label'] = label
+ count += 1
if(FLAGS.use_ipv6):
cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6)
From a9f9a0fcb7443b93db3f4de8f68218f20f0cc1a9 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月20日 18:36:18 -0600
Subject: [PATCH 006/109] really added migration for networks label
---
.../migrate_repo/versions/003_cactus.py | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
new file mode 100644
index 000000000000..13b4766d88f6
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
@@ -0,0 +1,47 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 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.
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+
+#
+# New Tables
+#
+
+
+#
+# Tables to alter
+#
+
+networks_label = Column(
+ 'label',
+ 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
+ networks.create_column(networks_label)
From d4a643976adbe49ec52db53694481e9ba687cddf Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月20日 18:40:04 -0600
Subject: [PATCH 007/109] fixed the migration
---
nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
index 13b4766d88f6..ddfe114cb9ae 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
@@ -25,6 +25,11 @@ from nova import log as logging
meta = MetaData()
+networks = Table('networks', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+
#
# New Tables
#
From 7ef1c34c2251eb32ef2effa58ea7ee85f46112f7 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月20日 18:44:00 -0600
Subject: [PATCH 008/109] moved argument for label
---
bin/nova-manage | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 38d36ab0f4a0..73832b0eb794 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -441,8 +441,8 @@ class NetworkCommands(object):
"""Class for managing networks."""
def create(self, fixed_range=None, num_networks=None,
- network_size=None, vlan_start=None, vpn_start=None,
- fixed_range_v6=None, label='public'):
+ network_size=None, label='public', vlan_start=None,
+ vpn_start=None, fixed_range_v6=None):
"""Creates fixed ips for host by range
arguments: [fixed_range=FLAG], [num_networks=FLAG],
[network_size=FLAG], [vlan_start=FLAG],
From 00b029f60baca843487b3cfd89940ed65e85389a Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月20日 18:51:46 -0600
Subject: [PATCH 009/109] undid moving argument
---
bin/nova-manage | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 73832b0eb794..9603c6a497c8 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -441,8 +441,8 @@ class NetworkCommands(object):
"""Class for managing networks."""
def create(self, fixed_range=None, num_networks=None,
- network_size=None, label='public', vlan_start=None,
- vpn_start=None, fixed_range_v6=None):
+ network_size=None, vlan_start=None,
+ vpn_start=None, fixed_range_v6=None, label='public'):
"""Creates fixed ips for host by range
arguments: [fixed_range=FLAG], [num_networks=FLAG],
[network_size=FLAG], [vlan_start=FLAG],
From 0c7893e4119bcccdfdfdcdef0931fcc8802688e8 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年1月26日 14:59:17 -0600
Subject: [PATCH 010/109] added mapping parameter to
write_network_config_to_xenstore
---
nova/virt/xenapi/vmops.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index c7310987bd6f..68fa1ecd619f 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -391,7 +391,7 @@ class VMOps(object):
args = {'id': str(uuid.uuid4())}
resp = self._make_agent_call('resetnetwork', vm, '', args)
- def write_network_config_to_xenstore(self, instance):
+ def write_network_config_to_xenstore(self, instance, mapping):
vm = self._get_vm_opaque_ref(instance)
self.write_to_param_xenstore(vm, mapping)
From eabc4c00eea8859c37efed3f180edbc41fd3b71d Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年1月27日 23:53:28 -0600
Subject: [PATCH 011/109] Working on api / manager / db support for zones
---
bin/nova-manage | 10 ++++++++++
nova/api/openstack/__init__.py | 5 +++++
nova/db/api.py | 8 ++++++++
nova/db/sqlalchemy/api.py | 9 +++++++++
4 files changed, 32 insertions(+)
diff --git a/bin/nova-manage b/bin/nova-manage
index 7835ca551ac5..b62687aec70d 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -569,6 +569,15 @@ class DbCommands(object):
print migration.db_version()
+class ZoneCommands(object):
+ """Methods for defining zones."""
+
+ def create(self, name):
+ """Create a new Zone for this deployment."""
+ ctxt = context.get_admin_context()
+ db.create_zone(ctxt, name)
+
+
class VolumeCommands(object):
"""Methods for dealing with a cloud in an odd state"""
@@ -620,6 +629,7 @@ CATEGORIES = [
('service', ServiceCommands),
('log', LogCommands),
('db', DbCommands),
+ ('zone', ZoneCommands),
('volume', VolumeCommands)]
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index c70bb39ed803..025fa12a4abb 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -34,6 +34,7 @@ from nova.api.openstack import flavors
from nova.api.openstack import images
from nova.api.openstack import servers
from nova.api.openstack import shared_ip_groups
+from nova.api.openstack import zones
LOG = logging.getLogger('nova.api.openstack')
@@ -79,6 +80,10 @@ class APIRouter(wsgi.Router):
server_members["actions"] = "GET"
server_members['suspend'] = 'POST'
server_members['resume'] = 'POST'
+
+ mapper.resource("zone", "zones", controller=zones.Controller(),
+ collection={'detail': 'GET'},
+ member=zone_members)
mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'},
diff --git a/nova/db/api.py b/nova/db/api.py
index 789cb8ebbac6..dc35f20b2277 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -980,3 +980,11 @@ def console_get_all_by_instance(context, instance_id):
def console_get(context, console_id, instance_id=None):
"""Get a specific console (possibly on a given instance)."""
return IMPL.console_get(context, console_id, instance_id)
+
+
+####################
+
+
+def create_zone(context, name):
+ """Create a new Zone entry for this deployment."""
+ return IMPL.create_zone(context, name)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 895e7eabede0..ec36c481ef94 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1996,3 +1996,12 @@ def console_get(context, console_id, instance_id=None):
raise exception.NotFound(_("No console with id %(console_id)s"
" %(idesc)s") % locals())
return result
+
+
+##################
+
+
+@require_admin_context
+def create_zone(context, zone):
+ session = get_session()
+ print "Creating Zone", zone
From f76a0a9d4d8aa3d8cc2669da1a8eea7d610a8616 Mon Sep 17 00:00:00 2001
From: termie
Date: 2011年1月30日 15:55:48 -0800
Subject: [PATCH 012/109] trivial cleanup for context.py
---
nova/context.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/nova/context.py b/nova/context.py
index f2669c9f1785..76e46703adbc 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -28,7 +28,6 @@ from nova import utils
class RequestContext(object):
-
def __init__(self, user, project, is_admin=None, read_deleted=False,
remote_address=None, timestamp=None, request_id=None):
if hasattr(user, 'id'):
@@ -53,7 +52,7 @@ class RequestContext(object):
self.read_deleted = read_deleted
self.remote_address = remote_address
if not timestamp:
- timestamp = datetime.datetime.utcnow()
+ timestampe = utils.utcnow()
if isinstance(timestamp, str) or isinstance(timestamp, unicode):
timestamp = utils.parse_isotime(timestamp)
self.timestamp = timestamp
@@ -101,7 +100,7 @@ class RequestContext(object):
return cls(**values)
def elevated(self, read_deleted=False):
- """Return a version of this context with admin flag set"""
+ """Return a version of this context with admin flag set."""
return RequestContext(self.user_id,
self.project_id,
True,
From 701c71999a135996575dd76a7171eb707b4d74ef Mon Sep 17 00:00:00 2001
From: termie
Date: 2011年1月30日 16:04:52 -0800
Subject: [PATCH 013/109] woops
---
nova/context.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/context.py b/nova/context.py
index 76e46703adbc..0256bf448dba 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -52,7 +52,7 @@ class RequestContext(object):
self.read_deleted = read_deleted
self.remote_address = remote_address
if not timestamp:
- timestampe = utils.utcnow()
+ timestamp = utils.utcnow()
if isinstance(timestamp, str) or isinstance(timestamp, unicode):
timestamp = utils.parse_isotime(timestamp)
self.timestamp = timestamp
From 620eba09a96f25a059249c23a5e73efd18aaf89a Mon Sep 17 00:00:00 2001
From: Trey Morris Date: Tue, 1 Feb 2011 14:11:21 -0600
Subject: [PATCH 014/109] forgot context param for network_get_all
---
nova/virt/xenapi/vmops.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 68fa1ecd619f..da2e5c672b18 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -95,9 +95,10 @@ class VMOps(object):
VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True)
# write network info
- network = db.network_get_by_instance(context.get_admin_context(),
+ admin_context = context.get_admin_context()
+ network = db.network_get_by_instance(admin_context,
instance['id'])
- for network in db.network_get_all():
+ for network in db.network_get_all(admin_context):
mapping = {'label': network['label'],
'gateway': network['gateway'],
'mac': instance.mac_address,
From 0e6b1c02b3ae82526f3cf83ce70213e7a107701d Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Tue, 1 Feb 2011 15:41:53 -0600
Subject: [PATCH 015/109] added to inject networking data into the xenstore
---
nova/virt/xenapi/vmops.py | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index da2e5c672b18..6edeae5c0a32 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -99,14 +99,16 @@ class VMOps(object):
network = db.network_get_by_instance(admin_context,
instance['id'])
for network in db.network_get_all(admin_context):
+ mac_id = instance.mac_address.replace(':', '')
+ location = 'vm-data/networking/%s' % mac_id
mapping = {'label': network['label'],
'gateway': network['gateway'],
'mac': instance.mac_address,
- 'dns': network['dns'],
+ 'dns': [network['dns']],
'ips': [{'netmask': network['netmask'],
'enabled': '1',
'ip': '192.168.3.3'}]} # <===== CHANGE!!!! - self.write_network_config_to_xenstore(vm_ref, mapping) + self.write_to_param_xenstore(vm_ref, {location: mapping}) bridge = network['bridge'] network_ref = \ @@ -392,10 +394,6 @@ class VMOps(object): args = {'id': str(uuid.uuid4())} resp = self._make_agent_call('resetnetwork', vm, '', args) - def write_network_config_to_xenstore(self, instance, mapping): - vm = self._get_vm_opaque_ref(instance) - self.write_to_param_xenstore(vm, mapping) - def list_from_xenstore(self, vm, path): """Runs the xenstore-ls command to get a listing of all records from 'path' downward. Returns a dict with the sub-paths as keys, From 4b121d03b27fd2ab3e5a2df0833ff8dfae5dfad3 Mon Sep 17 00:00:00 2001 From: termie
Date: Wed, 2 Feb 2011 13:13:10 -0800
Subject: [PATCH 016/109] some updates to HACKING to describe the docstrings
---
HACKING | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/HACKING b/HACKING
index 3af2381bfb83..4e0dfc8356f1 100644
--- a/HACKING
+++ b/HACKING
@@ -47,3 +47,18 @@ Human Alphabetical Order Examples
from nova.auth import users
from nova.endpoint import api
from nova.endpoint import cloud
+
+Docstrings
+----------
+ """Summary of the function, class or method, less than 80 characters.
+
+ New paragraph after newline that explains in more detail any general
+ information about the function, class or method. After this, if defining
+ parameters and return types use the Sphinx format. After that an extra
+ newline then close the quotations.
+
+ :param foo: the foo parameter
+ :param bar: the bar parameter
+ :rtype: the return type
+
+ """
From 3cae5a2573c96900f224d0145cee5077b01424b5 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Fri, 4 Feb 2011 11:36:09 -0600
Subject: [PATCH 017/109] Don't swallow exception stack traces by doing 'raise
e'; just use 'raise'
---
nova/compute/api.py | 4 ++--
nova/volume/manager.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index ac02dbcfa882..b409dc1e08da 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -70,7 +70,7 @@ class API(base.Base):
except exception.NotFound as e:
LOG.warning(_("Instance %d was not found in get_network_topic"),
instance_id)
- raise e
+ raise
host = instance['host']
if not host:
@@ -296,7 +296,7 @@ class API(base.Base):
except exception.NotFound as e:
LOG.warning(_("Instance %d was not found during terminate"),
instance_id)
- raise e
+ raise
if (instance['state_description'] == 'terminating'):
LOG.warning(_("Instance %d is already being terminated"),
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 6f8e25e19837..3e63e7b26e2a 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -114,7 +114,7 @@ class VolumeManager(manager.Manager):
except Exception as e:
self.db.volume_update(context,
volume_ref['id'], {'status': 'error'})
- raise e
+ raise
now = datetime.datetime.utcnow()
self.db.volume_update(context,
@@ -141,7 +141,7 @@ class VolumeManager(manager.Manager):
self.db.volume_update(context,
volume_ref['id'],
{'status': 'error_deleting'})
- raise e
+ raise
self.db.volume_destroy(context, volume_id)
LOG.debug(_("volume %s: deleted successfully"), volume_ref['name'])
From 645f4733081dfe03554cc30221ccc1a8b359d1ea Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Fri, 4 Feb 2011 16:20:24 -0600
Subject: [PATCH 018/109] Removed (newly) unused exception variables
---
nova/compute/api.py | 4 ++--
nova/volume/manager.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index b409dc1e08da..4a7c1c9e95fc 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -67,7 +67,7 @@ class API(base.Base):
"""Get the network topic for an instance."""
try:
instance = self.get(context, instance_id)
- except exception.NotFound as e:
+ except exception.NotFound:
LOG.warning(_("Instance %d was not found in get_network_topic"),
instance_id)
raise
@@ -293,7 +293,7 @@ class API(base.Base):
LOG.debug(_("Going to try to terminate %s"), instance_id)
try:
instance = self.get(context, instance_id)
- except exception.NotFound as e:
+ except exception.NotFound:
LOG.warning(_("Instance %d was not found during terminate"),
instance_id)
raise
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 3e63e7b26e2a..5771a638488f 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -111,7 +111,7 @@ class VolumeManager(manager.Manager):
LOG.debug(_("volume %s: creating export"), volume_ref['name'])
self.driver.create_export(context, volume_ref)
- except Exception as e:
+ except Exception:
self.db.volume_update(context,
volume_ref['id'], {'status': 'error'})
raise
@@ -137,7 +137,7 @@ class VolumeManager(manager.Manager):
self.driver.remove_export(context, volume_ref)
LOG.debug(_("volume %s: deleting"), volume_ref['name'])
self.driver.delete_volume(volume_ref)
- except Exception as e:
+ except Exception:
self.db.volume_update(context,
volume_ref['id'],
{'status': 'error_deleting'})
From 93d6050078214945fd2c842a15fb177f24811fa1 Mon Sep 17 00:00:00 2001
From: Salvatore Orlando
Date: Mon, 7 Feb 2011 17:06:30 +0000
Subject: [PATCH 019/109] Fix for bug #714709
---
nova/virt/xenapi/vm_utils.py | 5 +++++
nova/virt/xenapi/vmops.py | 19 +++++++++++++++++--
.../xenapi/etc/xapi.d/plugins/glance | 14 ++++++++++++--
3 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 4bbd522c1735..4f6de75888ef 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -438,7 +438,12 @@ class VMHelper(HelperBase):
return vdis
else:
return None
+ @classmethod
+ def lookup_kernel_ramdisk(cls,session,vm):
+ vm_rec = session.get_xenapi().VM.get_record(vm)
+ return (vm_rec['PV_kernel'],vm_rec['PV_ramdisk'])
+
@classmethod
def compile_info(cls, record):
"""Fill record with VM status information"""
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index e84ce20c4964..fe95d881b015 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -286,8 +286,23 @@ class VMOps(object):
def _destroy_vm(self, instance, vm):
"""Destroys a VM record """
try:
- task = self._session.call_xenapi('Async.VM.destroy', vm)
- self._session.wait_for_task(instance.id, task)
+ kernel = None
+ ramdisk = None
+ if instance.kernel_id or instance.ramdisk_id:
+ (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk(
+ self._session, vm)
+ task1 = self._session.call_xenapi('Async.VM.destroy', vm)
+ LOG.debug(_("Removing kernel/ramdisk files"))
+ fn = "remove_kernel_ramdisk"
+ args = {}
+ if kernel:
+ args['kernel-file'] = kernel
+ if ramdisk:
+ args['ramdisk-file'] = ramdisk
+ task2 = self._session.async_call_plugin('glance', fn, args)
+ self._session.wait_for_task(instance.id, task1)
+ self._session.wait_for_task(instance.id, task2)
+ LOG.debug(_("kernel/ramdisk files removed"))
except self.XenAPI.Failure, exc:
LOG.exception(exc)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index aadacce57456..4fb96eeab0eb 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -43,6 +43,16 @@ CHUNK_SIZE = 8192
KERNEL_DIR = '/boot/guest'
FILE_SR_PATH = '/var/run/sr-mount'
+def remove_kernel_ramdisk(session,args):
+ """Removes kernel and/or ramdisk from dom0's file system"""
+ kernel_file=exists(args,'kernel-file')
+ ramdisk_file=exists(args,'ramdisk-file')
+ if kernel_file:
+ os.remove(kernel_file)
+ if ramdisk_file:
+ os.remove(ramdisk_file)
+ return "ok"
+
def copy_kernel_vdi(session,args):
vdi = exists(args, 'vdi-ref')
size = exists(args,'image-size')
@@ -117,7 +127,6 @@ def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port):
while chunk:
conn.send(chunk)
chunk = bundle.read(CHUNK_SIZE)
-
res = conn.getresponse()
#FIXME(sirp): should this be 201 Created?
@@ -157,4 +166,5 @@ def find_sr(session):
if __name__ == '__main__':
XenAPIPlugin.dispatch({'put_vdis': put_vdis,
- 'copy_kernel_vdi': copy_kernel_vdi})
+ 'copy_kernel_vdi': copy_kernel_vdi,
+ 'remove_kernel_ramdisk': remove_kernel_ramdisk})
From 41e615b843c284631a0d878db2c93ef97f2eb4b8 Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: Mon, 7 Feb 2011 14:46:54 -0400
Subject: [PATCH 020/109] minor
---
nova/api/openstack/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 025fa12a4abb..8901a8987e2b 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -83,7 +83,7 @@ class APIRouter(wsgi.Router):
mapper.resource("zone", "zones", controller=zones.Controller(),
collection={'detail': 'GET'},
- member=zone_members)
+ collection_name='zones')
mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'},
From b6022c1f7d7dc9294f6b1b613c7e99bd9437a72e Mon Sep 17 00:00:00 2001
From: Trey Morris Date: Mon, 7 Feb 2011 13:43:23 -0600
Subject: [PATCH 021/109] added network_get_all_by_instance(), call to
reset_network in vmops
---
nova/db/sqlalchemy/api.py | 19 +++++++++++++------
nova/virt/xenapi/vmops.py | 11 +++++++----
2 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 31865d553365..26b685e434b5 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1055,12 +1055,6 @@ def network_get(context, network_id, session=None):
return result
-@require_context
-def network_get_all(context):
- session = get_session()
- return session.query(models.Network).all()
-
-
# NOTE(vish): pylint complains because of the long method name, but
# it fits with the names of the rest of the methods
# pylint: disable-msg=C0103
@@ -1104,6 +1098,19 @@ def network_get_by_instance(_context, instance_id):
return rv
+@require_admin_context
+def network_get_all_by_instance(_context, instance_id):
+ session = get_session()
+ rv = session.query(models.Network).\
+ filter_by(deleted=False).\
+ join(models.Network.fixed_ips).\
+ filter_by(instance_id=instance_id).\
+ filter_by(deleted=False)
+ if not rv:
+ raise exception.NotFound(_('No network for instance %s') % instance_id)
+ return rv
+
+
@require_admin_context
def network_set_host(context, network_id, host_id):
session = get_session()
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 6edeae5c0a32..4056e99bc0f1 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -96,9 +96,11 @@ class VMOps(object):
# write network info
admin_context = context.get_admin_context()
- network = db.network_get_by_instance(admin_context,
- instance['id'])
- for network in db.network_get_all(admin_context):
+ #network = db.network_get_by_instance(admin_context,
+ # instance['id'])
+
+ for network in db.network_get_all_by_instance(admin_context,
+ instance['id']):
mac_id = instance.mac_address.replace(':', '')
location = 'vm-data/networking/%s' % mac_id
mapping = {'label': network['label'],
@@ -119,6 +121,7 @@ class VMOps(object):
network_ref, instance.mac_address)
# call reset networking
+ self.reset_network(vm_ref)
LOG.debug(_('Starting VM %s...'), vm_ref)
self._session.call_xenapi('VM.start', vm_ref, False, False)
@@ -389,7 +392,7 @@ class VMOps(object):
# TODO: implement this!
return 'http://fakeajaxconsole/fake_url'
- def reset_networking(self, instance):
+ def reset_network(self, instance):
vm = self._get_vm_opaque_ref(instance)
args = {'id': str(uuid.uuid4())}
resp = self._make_agent_call('resetnetwork', vm, '', args)
From 2ecd9de1a0b26cd7168a008f44caad47ecb49c01 Mon Sep 17 00:00:00 2001
From: Salvatore Orlando
Date: Tue, 8 Feb 2011 10:14:51 +0000
Subject: [PATCH 022/109] Fixed pep8 errors Unit tests passed
---
nova/virt/xenapi/vm_utils.py | 13 ++--
.../xenserver/xenapi/etc/xapi.d/plugins/agent | 7 ++-
.../xenapi/etc/xapi.d/plugins/glance | 63 ++++++++++---------
.../xenapi/etc/xapi.d/plugins/objectstore | 46 +++++++-------
4 files changed, 72 insertions(+), 57 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 4f6de75888ef..2d22008055d1 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -438,12 +438,15 @@ class VMHelper(HelperBase):
return vdis
else:
return None
- @classmethod
- def lookup_kernel_ramdisk(cls,session,vm):
- vm_rec = session.get_xenapi().VM.get_record(vm)
- return (vm_rec['PV_kernel'],vm_rec['PV_ramdisk'])
-
+ @classmethod
+ def lookup_kernel_ramdisk(cls, session, vm):
+ vm_rec = session.get_xenapi().VM.get_record(vm)
+ if 'PV_kernel' in vm_rec and 'PV_ramdisk' in vm_rec:
+ return (vm_rec['PV_kernel'], vm_rec['PV_ramdisk'])
+ else:
+ return (None, None)
+
@classmethod
def compile_info(cls, record):
"""Fill record with VM status information"""
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
index 12c3a19c8d0c..031a49708d1c 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
@@ -73,8 +73,8 @@ def key_init(self, arg_dict):
@jsonify
def password(self, arg_dict):
"""Writes a request to xenstore that tells the agent to set
- the root password for the given VM. The password should be
- encrypted using the shared secret key that was returned by a
+ the root password for the given VM. The password should be
+ encrypted using the shared secret key that was returned by a
previous call to key_init. The encrypted password value should
be passed as the value for the 'enc_pass' key in arg_dict.
"""
@@ -108,7 +108,8 @@ def _wait_for_agent(self, request_id, arg_dict):
# First, delete the request record
arg_dict["path"] = "data/host/%s" % request_id
xenstore.delete_record(self, arg_dict)
- raise TimeoutError("TIMEOUT: No response from agent within %s seconds." %
+ raise TimeoutError(
+ "TIMEOUT: No response from agent within %s seconds." %
AGENT_TIMEOUT)
ret = xenstore.read_record(self, arg_dict)
# Note: the response for None with be a string that includes
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index 4fb96eeab0eb..8cb439259f7f 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -43,42 +43,47 @@ CHUNK_SIZE = 8192
KERNEL_DIR = '/boot/guest'
FILE_SR_PATH = '/var/run/sr-mount'
-def remove_kernel_ramdisk(session,args):
+
+def remove_kernel_ramdisk(session, args):
"""Removes kernel and/or ramdisk from dom0's file system"""
- kernel_file=exists(args,'kernel-file')
- ramdisk_file=exists(args,'ramdisk-file')
+ kernel_file = exists(args, 'kernel-file')
+ ramdisk_file = exists(args, 'ramdisk-file')
if kernel_file:
os.remove(kernel_file)
if ramdisk_file:
os.remove(ramdisk_file)
return "ok"
-def copy_kernel_vdi(session,args):
+
+def copy_kernel_vdi(session, args):
vdi = exists(args, 'vdi-ref')
- size = exists(args,'image-size')
+ size = exists(args, 'image-size')
#Use the uuid as a filename
- vdi_uuid=session.xenapi.VDI.get_uuid(vdi)
- copy_args={'vdi_uuid':vdi_uuid,'vdi_size':int(size)}
- filename=with_vdi_in_dom0(session, vdi, False,
+ vdi_uuid = session.xenapi.VDI.get_uuid(vdi)
+ copy_args = {'vdi_uuid': vdi_uuid, 'vdi_size': int(size)}
+ filename = with_vdi_in_dom0(session, vdi, False,
lambda dev:
- _copy_kernel_vdi('/dev/%s' % dev,copy_args))
+ _copy_kernel_vdi('/dev/%s' % dev, copy_args))
return filename
-def _copy_kernel_vdi(dest,copy_args):
- vdi_uuid=copy_args['vdi_uuid']
- vdi_size=copy_args['vdi_size']
- logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s",dest,vdi_uuid)
- filename=KERNEL_DIR + '/' + vdi_uuid
+
+def _copy_kernel_vdi(dest, copy_args):
+ vdi_uuid = copy_args['vdi_uuid']
+ vdi_size = copy_args['vdi_size']
+ logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s",
+ dest, vdi_uuid)
+ filename = KERNEL_DIR + '/' + vdi_uuid
#read data from /dev/ and write into a file on /boot/guest
- of=open(filename,'wb')
- f=open(dest,'rb')
+ of = open(filename, 'wb')
+ f = open(dest, 'rb')
#copy only vdi_size bytes
- data=f.read(vdi_size)
+ data = f.read(vdi_size)
of.write(data)
f.close()
- of.close()
- logging.debug("Done. Filename: %s",filename)
- return filename
+ of.close()
+ logging.debug("Done. Filename: %s", filename)
+ return filename
+
def put_vdis(session, args):
params = pickle.loads(exists(args, 'params'))
@@ -86,22 +91,23 @@ def put_vdis(session, args):
image_id = params["image_id"]
glance_host = params["glance_host"]
glance_port = params["glance_port"]
-
+
sr_path = get_sr_path(session)
#FIXME(sirp): writing to a temp file until Glance supports chunked-PUTs
- tmp_file = "%s.tar.gz" % os.path.join('/tmp', str(image_id))
+ tmp_file = "%s.tar.gz" % os.path.join('/tmp', str(image_id))
tar_cmd = ['tar', '-zcf', tmp_file, '--directory=%s' % sr_path]
- paths = [ "%s.vhd" % vdi_uuid for vdi_uuid in vdi_uuids ]
+ paths = ["%s.vhd" % vdi_uuid for vdi_uuid in vdi_uuids]
tar_cmd.extend(paths)
logging.debug("Bundling image with cmd: %s", tar_cmd)
subprocess.call(tar_cmd)
- logging.debug("Writing to test file %s", tmp_file)
+ logging.debug("Writing to test file %s", tmp_file)
put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port)
- return "" # FIXME(sirp): return anything useful here?
+ # FIXME(sirp): return anything useful here?
+ return ""
def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port):
- size = os.path.getsize(tmp_file)
+ size = os.path.getsize(tmp_file)
basename = os.path.basename(tmp_file)
bundle = open(tmp_file, 'r')
@@ -122,7 +128,7 @@ def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port):
for header, value in headers.iteritems():
conn.putheader(header, value)
conn.endheaders()
-
+
chunk = bundle.read(CHUNK_SIZE)
while chunk:
conn.send(chunk)
@@ -135,6 +141,7 @@ def put_bundle_in_glance(tmp_file, image_id, glance_host, glance_port):
finally:
bundle.close()
+
def get_sr_path(session):
sr_ref = find_sr(session)
@@ -165,6 +172,6 @@ def find_sr(session):
if __name__ == '__main__':
- XenAPIPlugin.dispatch({'put_vdis': put_vdis,
+ XenAPIPlugin.dispatch({'put_vdis': put_vdis,
'copy_kernel_vdi': copy_kernel_vdi,
'remove_kernel_ramdisk': remove_kernel_ramdisk})
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore
index 8ee2f748d710..d0313b4ed149 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/objectstore
@@ -43,34 +43,37 @@ SECTOR_SIZE = 512
MBR_SIZE_SECTORS = 63
MBR_SIZE_BYTES = MBR_SIZE_SECTORS * SECTOR_SIZE
-def is_vdi_pv(session,args):
+
+def is_vdi_pv(session, args):
logging.debug("Checking wheter VDI has PV kernel")
vdi = exists(args, 'vdi-ref')
- pv=with_vdi_in_dom0(session, vdi, False,
+ pv = with_vdi_in_dom0(session, vdi, False,
lambda dev: _is_vdi_pv('/dev/%s' % dev))
if pv:
return 'true'
else:
return 'false'
+
def _is_vdi_pv(dest):
- logging.debug("Running pygrub against %s",dest)
- output=os.popen('pygrub -qn %s' % dest)
- pv=False
+ logging.debug("Running pygrub against %s", dest)
+ output = os.popen('pygrub -qn %s' % dest)
+ pv = False
for line in output.readlines():
#try to find kernel string
- m=re.search('(?<=kernel:)/.*(?:>)',line)
+ m = re.search('(?<=kernel:)/.*(?:>)', line)
if m:
- if m.group(0).find('xen')!=-1:
- pv=True
- logging.debug("PV:%d",pv)
- return pv
-
+ if m.group(0).find('xen') != -1:
+ pv = True
+ logging.debug("PV:%d", pv)
+ return pv
+
+
def get_vdi(session, args):
src_url = exists(args, 'src_url')
username = exists(args, 'username')
password = exists(args, 'password')
- raw_image=validate_bool(args, 'raw', 'false')
+ raw_image = validate_bool(args, 'raw', 'false')
add_partition = validate_bool(args, 'add_partition', 'false')
(proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url)
sr = find_sr(session)
@@ -88,16 +91,17 @@ def get_vdi(session, args):
vdi = create_vdi(session, sr, src_url, vdi_size, False)
with_vdi_in_dom0(session, vdi, False,
lambda dev: get_vdi_(proto, netloc, url_path,
- username, password, add_partition,raw_image,
+ username, password,
+ add_partition, raw_image,
virtual_size, '/dev/%s' % dev))
return session.xenapi.VDI.get_uuid(vdi)
-def get_vdi_(proto, netloc, url_path, username, password, add_partition,raw_image,
- virtual_size, dest):
+def get_vdi_(proto, netloc, url_path, username, password,
+ add_partition, raw_image, virtual_size, dest):
- #Salvatore: vdi should not be partitioned for raw images
- if (add_partition and not raw_image):
+ #vdi should not be partitioned for raw images
+ if add_partition and not raw_image:
write_partition(virtual_size, dest)
offset = (add_partition and not raw_image and MBR_SIZE_BYTES) or 0
@@ -144,7 +148,7 @@ def get_kernel(session, args):
password = exists(args, 'password')
(proto, netloc, url_path, _, _, _) = urlparse.urlparse(src_url)
-
+
dest = os.path.join(KERNEL_DIR, url_path[1:])
# Paranoid check against people using ../ to do rude things.
@@ -154,8 +158,8 @@ def get_kernel(session, args):
dirname = os.path.dirname(dest)
try:
os.makedirs(dirname)
- except os.error, e:
- if e.errno != errno.EEXIST:
+ except os.error, e:
+ if e.errno != errno.EEXIST:
raise
if not os.path.isdir(dirname):
raise Exception('Cannot make directory %s', dirname)
@@ -248,5 +252,5 @@ def download_all(response, length, dest_file, offset):
if __name__ == '__main__':
XenAPIPlugin.dispatch({'get_vdi': get_vdi,
- 'get_kernel': get_kernel,
+ 'get_kernel': get_kernel,
'is_vdi_pv': is_vdi_pv})
From 653ff11692fa5cd5ec5f9ea75cddc03df1b3dcd5 Mon Sep 17 00:00:00 2001
From: Salvatore Orlando
Date: Tue, 8 Feb 2011 16:39:46 +0000
Subject: [PATCH 023/109] avoiding HOST_UNAVAILABLE exception: if there is not
enough free memory does not spawn the VM at all. instance state is set to
"SHUTDOWN"
---
nova/virt/xenapi/vm_utils.py | 11 +++++++++++
nova/virt/xenapi/vmops.py | 9 ++++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 4bbd522c1735..3a0f0a14977e 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -138,6 +138,17 @@ class VMHelper(HelperBase):
LOG.debug(_('Created VM %(instance_name)s as %(vm_ref)s.') % locals())
return vm_ref
+ @classmethod
+ def ensure_free_mem(cls, session, instance):
+ instance_type = instance_types.INSTANCE_TYPES[instance.instance_type]
+ mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
+ #get free memory from host
+ host = session.get_xenapi_host()
+ host_free_mem = session.get_xenapi().host.compute_free_memory(host)
+ if (host_free_mem < mem ): + return False + return True + @classmethod def create_vbd(cls, session, vm_ref, vdi_ref, userdevice, bootable): """Create a VBD record. Returns a Deferred that gives the new diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e84ce20c4964..2d4e5308334b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -66,7 +66,14 @@ class VMOps(object): if vm is not None: raise exception.Duplicate(_('Attempted to create' ' non-unique name %s') % instance.name) - + #ensure enough free memory, otherwise don't bother + if not VMHelper.ensure_free_mem(self._session,instance): + LOG.exception(_('instance %s: not enough free memory'), + instance['name']) + db.instance_set_state(context.get_admin_context(), + instance['id'], + power_state.SHUTDOWN) + return bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] network_ref = \ From f6ec9568561dd430bd772f171f5bbddd0bd038c6 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando
Date: Wed, 9 Feb 2011 10:08:15 +0000
Subject: [PATCH 024/109] Added test case for 'not enough memory' Successfully
ran unit tests Fixed pep8 errors
---
nova/compute/manager.py | 2 +-
nova/compute/power_state.py | 4 +++-
nova/tests/test_xenapi.py | 11 +++++++++--
nova/virt/xenapi/fake.py | 4 ++++
nova/virt/xenapi/vm_utils.py | 4 ++--
nova/virt/xenapi/vmops.py | 7 ++++---
6 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index f4418af26027..bb999931cf54 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -127,7 +127,7 @@ class ComputeManager(manager.Manager):
info = self.driver.get_info(instance_ref['name'])
state = info['state']
except exception.NotFound:
- state = power_state.NOSTATE
+ state = power_state.FAILED
self.db.instance_set_state(context, instance_id, state)
def get_console_topic(self, context, **_kwargs):
diff --git a/nova/compute/power_state.py b/nova/compute/power_state.py
index 37039d2ec9f9..adfc2dff005a 100644
--- a/nova/compute/power_state.py
+++ b/nova/compute/power_state.py
@@ -27,6 +27,7 @@ SHUTDOWN = 0x04
SHUTOFF = 0x05
CRASHED = 0x06
SUSPENDED = 0x07
+FAILED = 0x08
def name(code):
@@ -38,5 +39,6 @@ def name(code):
SHUTDOWN: 'shutdown',
SHUTOFF: 'shutdown',
CRASHED: 'crashed',
- SUSPENDED: 'suspended'}
+ SUSPENDED: 'suspended',
+ FAILED: 'failed to spawn'}
return d[code]
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 9f5b266f33ec..d5660c5d1f92 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -243,7 +243,8 @@ class XenAPIVMTestCase(test.TestCase):
# Check that the VM is running according to XenAPI.
self.assertEquals(vm['power_state'], 'Running')
- def _test_spawn(self, image_id, kernel_id, ramdisk_id):
+ def _test_spawn(self, image_id, kernel_id, ramdisk_id,
+ instance_type="m1.large"):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
values = {'name': 1,
'id': 1,
@@ -252,7 +253,7 @@ class XenAPIVMTestCase(test.TestCase):
'image_id': image_id,
'kernel_id': kernel_id,
'ramdisk_id': ramdisk_id,
- 'instance_type': 'm1.large',
+ 'instance_type': instance_type,
'mac_address': 'aa:bb:cc:dd:ee:ff',
}
conn = xenapi_conn.get_connection(False)
@@ -260,6 +261,12 @@ class XenAPIVMTestCase(test.TestCase):
conn.spawn(instance)
self.check_vm_record(conn)
+ def test_spawn_not_enough_memory(self):
+ FLAGS.xenapi_image_service = 'glance'
+ self.assertRaises(Exception,
+ self._test_spawn,
+ 1, 2, 3, "m1.xlarge")
+
def test_spawn_raw_objectstore(self):
FLAGS.xenapi_image_service = 'objectstore'
self._test_spawn(1, None, None)
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index e8352771c81f..018d0dcd3086 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -286,6 +286,10 @@ class SessionBase(object):
rec['currently_attached'] = False
rec['device'] = ''
+ def host_compute_free_memory(self, _1, ref):
+ #Always return 12GB available
+ return 12 * 1024 * 1024 * 1024
+
def xenapi_request(self, methodname, params):
if methodname.startswith('login'):
self._login(methodname, params)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 3a0f0a14977e..c6ac969b9c51 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -141,11 +141,11 @@ class VMHelper(HelperBase):
@classmethod
def ensure_free_mem(cls, session, instance):
instance_type = instance_types.INSTANCE_TYPES[instance.instance_type]
- mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
+ mem = long(instance_type['memory_mb']) * 1024 * 1024
#get free memory from host
host = session.get_xenapi_host()
host_free_mem = session.get_xenapi().host.compute_free_memory(host)
- if (host_free_mem < mem ): + if (host_free_mem < mem): return False return True diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 2d4e5308334b..e3c303d913e7 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -67,13 +67,13 @@ class VMOps(object): raise exception.Duplicate(_('Attempted to create' ' non-unique name %s') % instance.name) #ensure enough free memory, otherwise don't bother - if not VMHelper.ensure_free_mem(self._session,instance): + if not VMHelper.ensure_free_mem(self._session, instance): LOG.exception(_('instance %s: not enough free memory'), instance['name']) db.instance_set_state(context.get_admin_context(), instance['id'], power_state.SHUTDOWN) - return + return bridge = db.network_get_by_instance(context.get_admin_context(), instance['id'])['bridge'] network_ref = \ @@ -168,7 +168,8 @@ class VMOps(object): instance_name = instance_or_vm.name vm = VMHelper.lookup(self._session, instance_name) if vm is None: - raise Exception(_('Instance not present %s') % instance_name) + raise exception.NotFound( + _('Instance not present %s') % instance_name) return vm def snapshot(self, instance, image_id): From 9a55136eb691de8d795ec47c5720556160899244 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando
Date: Wed, 9 Feb 2011 15:39:37 +0000
Subject: [PATCH 025/109] Fixed pep8 error in vm_utils.py
---
nova/virt/xenapi/vm_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 2d22008055d1..f5c19099a9e8 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -445,7 +445,7 @@ class VMHelper(HelperBase):
if 'PV_kernel' in vm_rec and 'PV_ramdisk' in vm_rec:
return (vm_rec['PV_kernel'], vm_rec['PV_ramdisk'])
else:
- return (None, None)
+ return (None, None)
@classmethod
def compile_info(cls, record):
From 6e881239c9b8a1fb209868addf1a2b83042f2128 Mon Sep 17 00:00:00 2001
From: brian-lamar
Date: Wed, 9 Feb 2011 13:30:40 -0500
Subject: [PATCH 026/109] 1) Moved tests for limiter to test_common.py (from
__init__.py) and expanded test suite to include bad inputs and tests for
custom limits (#2)
2) Wrapped int() calls in blocks to ensure logic regardless of input.
3) Moved 1000 hard limit hard-coding to a keyword param.
4) Added comments as I went.
---
nova/api/openstack/common.py | 35 +++---
nova/tests/api/openstack/__init__.py | 28 -----
nova/tests/api/openstack/test_common.py | 161 ++++++++++++++++++++++++
3 files changed, 182 insertions(+), 42 deletions(-)
create mode 100644 nova/tests/api/openstack/test_common.py
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 6d2fa16e8ec5..1dc3767e2d44 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -18,22 +18,29 @@
from nova import exception
-def limited(items, req):
- """Return a slice of items according to requested offset and limit.
-
- items - a sliceable
- req - wobob.Request possibly containing offset and limit GET variables.
- offset is where to start in the list, and limit is the maximum number
- of items to return.
-
- If limit is not specified, 0, or> 1000, defaults to 1000.
+def limited(items, request, max_limit=1000):
"""
+ Return a slice of items according to requested offset and limit.
- offset = int(req.GET.get('offset', 0))
- limit = int(req.GET.get('limit', 0))
- if not limit:
- limit = 1000
- limit = min(1000, limit)
+ @param items: A sliceable entity
+ @param request: `webob.Request` possibly containing 'offset' and 'limit'
+ GET variables. 'offset' is where to start in the list,
+ and 'limit' is the maximum number of items to return. If
+ 'limit' is not specified, 0, or> max_limit, we default
+ to max_limit.
+ @kwarg max_limit: The maximum number of items to return from 'items'
+ """
+ try:
+ offset = int(request.GET.get('offset', 0))
+ except ValueError:
+ offset = 0
+
+ try:
+ limit = int(request.GET.get('limit', max_limit))
+ except ValueError:
+ limit = max_limit
+
+ limit = min(max_limit, limit or max_limit)
range_end = offset + limit
return items[offset:range_end]
diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py
index 14eaaa62c713..77b1dd37f40e 100644
--- a/nova/tests/api/openstack/__init__.py
+++ b/nova/tests/api/openstack/__init__.py
@@ -92,31 +92,3 @@ class RateLimitingMiddlewareTest(unittest.TestCase):
self.assertEqual(middleware.limiter.__class__.__name__, "Limiter")
middleware = RateLimitingMiddleware(simple_wsgi, service_host='foobar')
self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy")
-
-
-class LimiterTest(unittest.TestCase):
-
- def test_limiter(self):
- items = range(2000)
- req = Request.blank('/')
- self.assertEqual(limited(items, req), items[:1000])
- req = Request.blank('/?offset=0')
- self.assertEqual(limited(items, req), items[:1000])
- req = Request.blank('/?offset=3')
- self.assertEqual(limited(items, req), items[3:1003])
- req = Request.blank('/?offset=2005')
- self.assertEqual(limited(items, req), [])
- req = Request.blank('/?limit=10')
- self.assertEqual(limited(items, req), items[:10])
- req = Request.blank('/?limit=0')
- self.assertEqual(limited(items, req), items[:1000])
- req = Request.blank('/?limit=3000')
- self.assertEqual(limited(items, req), items[:1000])
- req = Request.blank('/?offset=1&limit=3')
- self.assertEqual(limited(items, req), items[1:4])
- req = Request.blank('/?offset=3&limit=0')
- self.assertEqual(limited(items, req), items[3:1003])
- req = Request.blank('/?offset=3&limit=1500')
- self.assertEqual(limited(items, req), items[3:1003])
- req = Request.blank('/?offset=3000&limit=10')
- self.assertEqual(limited(items, req), [])
diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py
new file mode 100644
index 000000000000..9d9837cc99a3
--- /dev/null
+++ b/nova/tests/api/openstack/test_common.py
@@ -0,0 +1,161 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 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.
+
+"""
+Test suites for 'common' code used throughout the OpenStack HTTP API.
+"""
+
+import unittest
+
+from webob import Request
+
+from nova.api.openstack.common import limited
+
+
+class LimiterTest(unittest.TestCase):
+ """
+ Unit tests for the `nova.api.openstack.common.limited` method which takes
+ in a list of items and, depending on the 'offset' and 'limit' GET params,
+ returns a subset or complete set of the given items.
+ """
+
+ def setUp(self):
+ """
+ Run before each test.
+ """
+ self.tiny = range(1)
+ self.small = range(10)
+ self.medium = range(1000)
+ self.large = range(10000)
+
+ def test_limiter_offset_zero(self):
+ """
+ Test offset key works with 0.
+ """
+ req = Request.blank('/?offset=0')
+ self.assertEqual(limited(self.tiny, req), self.tiny)
+ self.assertEqual(limited(self.small, req), self.small)
+ self.assertEqual(limited(self.medium, req), self.medium)
+ self.assertEqual(limited(self.large, req), self.large[:1000])
+
+ def test_limiter_offset_medium(self):
+ """
+ Test offset key works with a medium sized number.
+ """
+ req = Request.blank('/?offset=10')
+ self.assertEqual(limited(self.tiny, req), [])
+ self.assertEqual(limited(self.small, req), self.small[10:])
+ self.assertEqual(limited(self.medium, req), self.medium[10:])
+ self.assertEqual(limited(self.large, req), self.large[10:1010])
+
+ def test_limiter_offset_over_max(self):
+ """
+ Test offset key works with a number over 1000 (max_limit).
+ """
+ req = Request.blank('/?offset=1001')
+ self.assertEqual(limited(self.tiny, req), [])
+ self.assertEqual(limited(self.small, req), [])
+ self.assertEqual(limited(self.medium, req), [])
+ self.assertEqual(limited(self.large, req), self.large[1001:2001])
+
+ def test_limiter_offset_blank(self):
+ """
+ Test offset key works with a blank offset.
+ """
+ req = Request.blank('/?offset=')
+ self.assertEqual(limited(self.tiny, req), self.tiny)
+ self.assertEqual(limited(self.small, req), self.small)
+ self.assertEqual(limited(self.medium, req), self.medium)
+ self.assertEqual(limited(self.large, req), self.large[:1000])
+
+ def test_limiter_offset_bad(self):
+ """
+ Test offset key works with a BAD offset.
+ """
+ req = Request.blank(u'/?offset=\u0020aa')
+ self.assertEqual(limited(self.tiny, req), self.tiny)
+ self.assertEqual(limited(self.small, req), self.small)
+ self.assertEqual(limited(self.medium, req), self.medium)
+ self.assertEqual(limited(self.large, req), self.large[:1000])
+
+ def test_limiter_nothing(self):
+ """
+ Test request with no offset or limit
+ """
+ req = Request.blank('/')
+ self.assertEqual(limited(self.tiny, req), self.tiny)
+ self.assertEqual(limited(self.small, req), self.small)
+ self.assertEqual(limited(self.medium, req), self.medium)
+ self.assertEqual(limited(self.large, req), self.large[:1000])
+
+ def test_limiter_limit_zero(self):
+ """
+ Test limit of zero.
+ """
+ req = Request.blank('/?limit=0')
+ self.assertEqual(limited(self.tiny, req), self.tiny)
+ self.assertEqual(limited(self.small, req), self.small)
+ self.assertEqual(limited(self.medium, req), self.medium)
+ self.assertEqual(limited(self.large, req), self.large[:1000])
+
+ def test_limiter_limit_medium(self):
+ """
+ Test limit of 10.
+ """
+ req = Request.blank('/?limit=10')
+ self.assertEqual(limited(self.tiny, req), self.tiny)
+ self.assertEqual(limited(self.small, req), self.small)
+ self.assertEqual(limited(self.medium, req), self.medium[:10])
+ self.assertEqual(limited(self.large, req), self.large[:10])
+
+ def test_limiter_limit_over_max(self):
+ """
+ Test limit of 3000.
+ """
+ req = Request.blank('/?limit=3000')
+ self.assertEqual(limited(self.tiny, req), self.tiny)
+ self.assertEqual(limited(self.small, req), self.small)
+ self.assertEqual(limited(self.medium, req), self.medium)
+ self.assertEqual(limited(self.large, req), self.large[:1000])
+
+ def test_limiter_limit_and_offset(self):
+ """
+ Test request with both limit and offset.
+ """
+ items = range(2000)
+ req = Request.blank('/?offset=1&limit=3')
+ self.assertEqual(limited(items, req), items[1:4])
+ req = Request.blank('/?offset=3&limit=0')
+ self.assertEqual(limited(items, req), items[3:1003])
+ req = Request.blank('/?offset=3&limit=1500')
+ self.assertEqual(limited(items, req), items[3:1003])
+ req = Request.blank('/?offset=3000&limit=10')
+ self.assertEqual(limited(items, req), [])
+
+ def test_limiter_custom_max_limit(self):
+ """
+ Test a max_limit other than 1000.
+ """
+ items = range(2000)
+ req = Request.blank('/?offset=1&limit=3')
+ self.assertEqual(limited(items, req, max_limit=2000), items[1:4])
+ req = Request.blank('/?offset=3&limit=0')
+ self.assertEqual(limited(items, req, max_limit=2000), items[3:])
+ req = Request.blank('/?offset=3&limit=2500')
+ self.assertEqual(limited(items, req, max_limit=2000), items[3:])
+ req = Request.blank('/?offset=3000&limit=10')
+ self.assertEqual(limited(items, req, max_limit=2000), [])
From 590f5f1793c1f829101b4edbacbc79eac7acd2ef Mon Sep 17 00:00:00 2001
From: brian-lamar
Date: Wed, 9 Feb 2011 13:36:16 -0500
Subject: [PATCH 027/109] Added myself to Authors
---
Authors | 1 +
1 file changed, 1 insertion(+)
diff --git a/Authors b/Authors
index 27782738f015..14cc95377959 100644
--- a/Authors
+++ b/Authors
@@ -3,6 +3,7 @@ Anne Gentle
Anthony Young
Antony Messerli
Armando Migliaccio
+Brian Lamar
Chiradeep Vittal
Chmouel Boudjnah
Chris Behrens
From 52e1ad5321590b7b4671349373217bc8fce275fc Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Wed, 9 Feb 2011 15:55:29 -0500
Subject: [PATCH 028/109] - population of public and private addresses
containers in openstack api - replacement of sqlalchemy model in instance
stub with dict
---
nova/api/openstack/servers.py | 18 +++++++
nova/tests/api/openstack/test_servers.py | 66 ++++++++++++++++++++++--
2 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 17c5519a1a91..60f3d96e3c25 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -64,6 +64,24 @@ def _translate_detail_keys(inst):
inst_dict['status'] = power_mapping[inst_dict['status']]
inst_dict['addresses'] = dict(public=[], private=[])
+
+ # grab single private fixed ip
+ try:
+ private_ip = inst['fixed_ip']['address']
+ if private_ip:
+ inst_dict['addresses']['private'].append(private_ip)
+ except KeyError:
+ LOG.debug(_("Failed to read private ip"))
+ pass
+
+ # grab all public floating ips
+ try:
+ [inst_dict['addresses']['public'].append(floating['address']) \
+ for floating in inst['fixed_ip']['floating_ips']]
+ except KeyError:
+ LOG.debug(_("Failed to read public ip(s)"))
+ pass
+
inst_dict['metadata'] = {}
inst_dict['hostId'] = ''
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 724f14f19ccd..816a0ab8c6ed 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -17,6 +17,7 @@
import json
import unittest
+import datetime
import stubout
import webob
@@ -39,6 +40,13 @@ def return_server(context, id):
return stub_instance(id)
+def return_server_with_addresses(private, public):
+ def _return_server(context, id):
+ return stub_instance(id, private_address=private,
+ public_addresses=public)
+ return _return_server
+
+
def return_servers(context, user_id=1):
return [stub_instance(i, user_id) for i in xrange(5)]
@@ -55,9 +63,45 @@ def instance_address(context, instance_id):
return None
-def stub_instance(id, user_id=1):
- return Instance(id=id, state=0, image_id=10, user_id=user_id,
- display_name='server%s' % id)
+def stub_instance(id, user_id=1, private_address=None, public_addresses=None):
+ if public_addresses == None:
+ public_addresses = list()
+
+ instance = {
+ "id": id,
+ "admin_pass": "",
+ "user_id": user_id,
+ "project_id": "",
+ "image_id": 10,
+ "kernel_id": "",
+ "ramdisk_id": "",
+ "launch_index": 0,
+ "key_name": "",
+ "key_data": "",
+ "state": 0,
+ "state_description": "",
+ "memory_mb": 0,
+ "vcpus": 0,
+ "local_gb": 0,
+ "hostname": "",
+ "host": "",
+ "instance_type": "",
+ "user_data": "",
+ "reservation_id": "",
+ "mac_address": "",
+ "scheduled_at": datetime.datetime.now(),
+ "launched_at": datetime.datetime.now(),
+ "terminated_at": datetime.datetime.now(),
+ "availability_zone": "",
+ "display_name": "server%s" % id,
+ "display_description": "",
+ "locked": False}
+
+ instance["fixed_ip"] = {
+ "address": private_address,
+ "floating_ips": [{"address":ip} for ip in public_addresses]}
+
+ return instance
def fake_compute_api(cls, req, id):
@@ -105,6 +149,22 @@ class ServersTest(unittest.TestCase):
self.assertEqual(res_dict['server']['id'], '1')
self.assertEqual(res_dict['server']['name'], 'server1')
+ def test_get_server_by_id_with_addresses(self):
+ private = "192.168.0.3"
+ public = ["1.2.3.4"]
+ new_return_server = return_server_with_addresses(private, public)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+ req = webob.Request.blank('/v1.0/servers/1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ self.assertEqual(res_dict['server']['id'], '1')
+ self.assertEqual(res_dict['server']['name'], 'server1')
+ addresses = res_dict['server']['addresses']
+ self.assertEqual(len(addresses["public"]), len(public))
+ self.assertEqual(addresses["public"][0], public[0])
+ self.assertEqual(len(addresses["private"]), 1)
+ self.assertEqual(addresses["private"][0], private)
+
def test_get_server_list(self):
req = webob.Request.blank('/v1.0/servers')
res = req.get_response(fakes.wsgi_app())
From 4a4a3f04b78ba2cbaa0d02ecf0f7cd3cf580901b Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Wed, 9 Feb 2011 21:54:52 -0500
Subject: [PATCH 029/109] adding myself to Authors file
---
Authors | 1 +
1 file changed, 1 insertion(+)
diff --git a/Authors b/Authors
index 27782738f015..563ddf759252 100644
--- a/Authors
+++ b/Authors
@@ -3,6 +3,7 @@ Anne Gentle
Anthony Young
Antony Messerli
Armando Migliaccio
+Brian Waldon
Chiradeep Vittal
Chmouel Boudjnah
Chris Behrens
From 16ffc15b1fb45a09de14cece6b382357a030b9dc Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月10日 08:43:46 -0400
Subject: [PATCH 030/109] removed ZoneCommands from nova-manage
---
bin/nova-manage | 10 ----------
nova/api/openstack/__init__.py | 2 +-
2 files changed, 1 insertion(+), 11 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index b62687aec70d..7835ca551ac5 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -569,15 +569,6 @@ class DbCommands(object):
print migration.db_version()
-class ZoneCommands(object):
- """Methods for defining zones."""
-
- def create(self, name):
- """Create a new Zone for this deployment."""
- ctxt = context.get_admin_context()
- db.create_zone(ctxt, name)
-
-
class VolumeCommands(object):
"""Methods for dealing with a cloud in an odd state"""
@@ -629,7 +620,6 @@ CATEGORIES = [
('service', ServiceCommands),
('log', LogCommands),
('db', DbCommands),
- ('zone', ZoneCommands),
('volume', VolumeCommands)]
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 8901a8987e2b..69a4d66c00b1 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -80,7 +80,7 @@ class APIRouter(wsgi.Router):
server_members["actions"] = "GET"
server_members['suspend'] = 'POST'
server_members['resume'] = 'POST'
-
+
mapper.resource("zone", "zones", controller=zones.Controller(),
collection={'detail': 'GET'},
collection_name='zones')
From 51b7eb9fd9a56e58137c8be21ea002871bd65e30 Mon Sep 17 00:00:00 2001
From: Rick Harris
Date: 2011年2月10日 19:40:21 +0000
Subject: [PATCH 031/109] sql_idle_timeout should be an integer
---
nova/flags.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/flags.py b/nova/flags.py
index 1d8eba94fd5e..3ba3fe6faa1e 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -286,8 +286,8 @@ DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'),
DEFINE_string('sql_connection',
'sqlite:///$state_path/nova.sqlite',
'connection string for sql database')
-DEFINE_string('sql_idle_timeout',
- '3600',
+DEFINE_integer('sql_idle_timeout',
+ 3600,
'timeout for idle sql database connections')
DEFINE_integer('sql_max_retries', 12, 'sql connection attempts')
DEFINE_integer('sql_retry_interval', 10, 'sql connection retry interval')
From 389b548e332a496bcc74d637030f753c66add570 Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月10日 16:08:19 -0400
Subject: [PATCH 032/109] template adjusted to NOVA_TOOLS, zone db & os api
layers added
---
nova/api/openstack/zones.py | 75 ++++++++++++++++++++++++++++++++++++
nova/auth/novarc.template | 7 ++--
nova/db/api.py | 26 +++++++++++--
nova/db/sqlalchemy/api.py | 41 ++++++++++++++++++--
nova/db/sqlalchemy/models.py | 11 +++++-
5 files changed, 149 insertions(+), 11 deletions(-)
create mode 100644 nova/api/openstack/zones.py
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
new file mode 100644
index 000000000000..a12d1cc0c023
--- /dev/null
+++ b/nova/api/openstack/zones.py
@@ -0,0 +1,75 @@
+# Copyright 2010 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 logging
+
+from nova import flags
+from nova import wsgi
+from nova import db
+
+
+FLAGS = flags.FLAGS
+
+
+def _filter_keys(item, keys):
+ """
+ Filters all model attributes except for keys
+ item is a dict
+
+ """
+ return dict((k, v) for k, v in item.iteritems() if k in keys)
+
+
+class Controller(wsgi.Controller):
+
+ _serialization_metadata = {
+ 'application/xml': {
+ "attributes": {
+ "zone": ["id", "api_url"]}}}
+
+ def index(self, req):
+ """Return all zones in brief"""
+ items = db.zone_get_all(req.environ['nova.context'])
+ items = common.limited(items, req)
+ items = [_filter_keys(item, ('id', 'api_url')) for item in items]
+ return dict(zones=items)
+
+ def detail(self, req):
+ """Return all zones in detail"""
+ return self.index(req)
+
+ def show(self, req, id):
+ """Return data about the given zone id"""
+ zone_id = int(id)
+ zone = db.zone_get(req.environ['nova.context'], zone_id)
+ return dict(zone=zone)
+
+ def delete(self, req, id):
+ zone_id = int(id)
+ db.zone_delete(req.environ['nova.context'], zone_id)
+ return {}
+
+ def create(self, req):
+ context = req.environ['nova.context']
+ env = self._deserialize(req.body, req)
+ zone = db.zone_create(context, env["zone"])
+ return dict(zone=zone)
+
+ def update(self, req, id):
+ context = req.environ['nova.context']
+ env = self._deserialize(req.body, req)
+ zone_id = int(id)
+ zone = db.zone_update(context, zone_id, env["zone"])
+ return dict(zone=zone)
diff --git a/nova/auth/novarc.template b/nova/auth/novarc.template
index c53a4acdc287..702df3bb060b 100644
--- a/nova/auth/novarc.template
+++ b/nova/auth/novarc.template
@@ -10,7 +10,6 @@ export NOVA_CERT=${NOVA_KEY_DIR}/%(nova)s
export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set
alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}"
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}"
-export CLOUD_SERVERS_API_KEY="%(access)s"
-export CLOUD_SERVERS_USERNAME="%(user)s"
-export CLOUD_SERVERS_URL="%(os)s"
-
+export NOVA_TOOLS_API_KEY="%(access)s"
+export NOVA_TOOLS_USERNAME="%(user)s"
+export NOVA_TOOLS_URL="%(os)s"
diff --git a/nova/db/api.py b/nova/db/api.py
index dc35f20b2277..fa73d86ad3d6 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -985,6 +985,26 @@ def console_get(context, console_id, instance_id=None):
####################
-def create_zone(context, name):
- """Create a new Zone entry for this deployment."""
- return IMPL.create_zone(context, name)
+def zone_create(context, values):
+ """Create a new ChildZone entry in this Zone."""
+ return IMPL.zone_create(context, values)
+
+
+def zone_update(context, zone_id, values):
+ """Update a ChildZone entry in this Zone."""
+ return IMPL.zone_update(context, values)
+
+
+def zone_delete(context, zone_id):
+ """Delete a ChildZone."""
+ return IMPL.zone_delete(context, zone_id)
+
+
+def zone_get(context, zone_id):
+ """Get a specific ChildZone."""
+ return IMPL.zone_get(context, zone_id)
+
+
+def zone_get_all(context):
+ """Get all ChildZone's."""
+ return IMPL.zone_get_all(context)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index f568d3470305..cdd6db25fdbb 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -2010,10 +2010,45 @@ def console_get(context, console_id, instance_id=None):
return result
-##################
+####################
@require_admin_context
-def create_zone(context, zone):
+def zone_create(context, values):
+ zone = models.ChildZone()
+ zone.update(values)
+ zone.save()
+ return zone
+
+
+@require_admin_context
+def zone_update(context, zone_id, values):
+ zone = session.query(models.ChildZone).filter_by(id=zone_id).first()
+ if not zone:
+ raise exception.NotFound(_("No zone with id %(zone_id)s") % locals())
+ zone.update(values)
+ zone.save()
+ return zone
+
+
+@require_admin_context
+def zone_delete(context, zone_id):
session = get_session()
- print "Creating Zone", zone
+ with session.begin():
+ session.execute('delete from childzones '
+ 'where id=:id', {'id': zone_id})
+
+
+@require_admin_context
+def zone_get(context, zone_id):
+ session = get_session()
+ result = session.query(models.ChildZone).filter_by(id=zone_id).first()
+ if not result:
+ raise exception.NotFound(_("No zone with id %(zone_id)s") % locals())
+ return result
+
+
+@require_admin_context
+def zone_get_all(context):
+ session = get_session()
+ return session.query(models.ChildZone).filter_by(id=zone_id).all()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 7efb36c0e30b..3c677cad8356 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -535,6 +535,15 @@ class Console(BASE, NovaBase):
pool = relationship(ConsolePool, backref=backref('consoles'))
+class ChildZone(BASE, NovaBase):
+ """Represents a child zone of this zone."""
+ __tablename__ = 'childzones'
+ id = Column(Integer, primary_key=True)
+ api_url = Column(String(255))
+ username = Column(String(255))
+ password = Column(String(255))
+
+
def register_models():
"""Register Models and create metadata.
@@ -547,7 +556,7 @@ def register_models():
Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp,
Network, SecurityGroup, SecurityGroupIngressRule,
SecurityGroupInstanceAssociation, AuthToken, User,
- Project, Certificate, ConsolePool, Console) # , Image, Host
+ Project, Certificate, ConsolePool, Console, ChildZone)
engine = create_engine(FLAGS.sql_connection, echo=False)
for model in models:
model.metadata.create_all(engine)
From b0c6190e0b098af4d808d993c6dcd0796cc80e83 Mon Sep 17 00:00:00 2001
From: Trey Morris Date: 2011年2月10日 14:18:16 -0600
Subject: [PATCH 033/109] forgot to add network_get_all_by_instance to db.api
---
nova/db/api.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/nova/db/api.py b/nova/db/api.py
index f22cd561594a..a38f187a87d8 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -522,6 +522,11 @@ def network_get_by_instance(context, instance_id):
return IMPL.network_get_by_instance(context, instance_id)
+def network_get_all_by_instance(context, instance_id):
+ """Get all networks by instance id or raise if it does not exist."""
+ return IMPL.network_get_all_by_instance(context, instance_id)
+
+
def network_get_index(context, network_id):
"""Get non-conflicting index for network."""
return IMPL.network_get_index(context, network_id)
From 87d0b5203610f1e0a7a2e09033c79071fabacaba Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年2月10日 15:01:31 -0600
Subject: [PATCH 034/109] passing instance to reset_network instead of vm_ref,
also not converting to an opaque ref before making plugin call
---
nova/virt/xenapi/vmops.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 4056e99bc0f1..575e53f80ff0 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -121,7 +121,7 @@ class VMOps(object):
network_ref, instance.mac_address)
# call reset networking
- self.reset_network(vm_ref)
+ self.reset_network(instance)
LOG.debug(_('Starting VM %s...'), vm_ref)
self._session.call_xenapi('VM.start', vm_ref, False, False)
@@ -393,9 +393,8 @@ class VMOps(object):
return 'http://fakeajaxconsole/fake_url'
def reset_network(self, instance):
- vm = self._get_vm_opaque_ref(instance)
args = {'id': str(uuid.uuid4())}
- resp = self._make_agent_call('resetnetwork', vm, '', args)
+ resp = self._make_agent_call('resetnetwork', instance, '', args)
def list_from_xenstore(self, vm, path):
"""Runs the xenstore-ls command to get a listing of all records
From 96640472934c4eba48c6ab0048ac5bcf3c192eb4 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年2月10日 15:25:26 -0600
Subject: [PATCH 035/109] added resetnetwork to the XenAPIPlugin.dispatch dict
---
plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
index 5c5ec7c4571f..b4c74239686a 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
@@ -135,4 +135,5 @@ def _wait_for_agent(self, request_id, arg_dict):
if __name__ == "__main__":
XenAPIPlugin.dispatch(
{"key_init": key_init,
- "password": password})
+ "password": password,
+ "resetnetwork": resetnetwork})
From 57e58ba23c5c6a1af0f132385d3d9b9cc370b47d Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年2月10日 16:26:08 -0600
Subject: [PATCH 036/109] added get IPs by instance
---
nova/db/api.py | 5 +++++
nova/db/sqlalchemy/api.py | 11 +++++++++++
2 files changed, 16 insertions(+)
diff --git a/nova/db/api.py b/nova/db/api.py
index a38f187a87d8..a2c1dbdceb51 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -294,6 +294,11 @@ def fixed_ip_get_by_address(context, address):
return IMPL.fixed_ip_get_by_address(context, address)
+def fixed_ip_get_all_by_instance(context, instance_id):
+ """Get fixed ips by instance or raise if none exist."""
+ return IMPL.fixed_ip_get_all_by_instance(context, instance_id)
+
+
def fixed_ip_get_instance(context, address):
"""Get an instance for a fixed ip by address."""
return IMPL.fixed_ip_get_instance(context, address)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 26b685e434b5..f20f4e2667a7 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -606,6 +606,17 @@ def fixed_ip_get_instance(context, address):
return fixed_ip_ref.instance
+@require_context
+def fixed_ip_get_all_by_instance(context, instance_id):
+ session = get_session()
+ rv = session.query(models.Network.fixed_ips).\
+ filter_by(instance_id=instance_id).\
+ filter_by(deleted=False)
+ if not rv:
+ raise exception.NotFound(_('No address for instance %s') % instance_id)
+ return rv
+
+
@require_context
def fixed_ip_get_instance_v6(context, address):
session = get_session()
From c6ad6b53230d1c3ca540a1dcdcd1f3ebf4f31f07 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月11日 12:27:50 +0100
Subject: [PATCH 037/109] Create a new AMQP connection by default.
---
nova/rpc.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/nova/rpc.py b/nova/rpc.py
index c19ee46356be..2b1f7298b9c5 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -46,7 +46,7 @@ LOG = logging.getLogger('nova.rpc')
class Connection(carrot_connection.BrokerConnection):
"""Connection instance object"""
@classmethod
- def instance(cls, new=False):
+ def instance(cls, new=True):
"""Returns the instance"""
if new or not hasattr(cls, '_instance'):
params = dict(hostname=FLAGS.rabbit_host,
@@ -246,7 +246,7 @@ def msg_reply(msg_id, reply=None, failure=None):
LOG.error(_("Returning exception %s to caller"), message)
LOG.error(tb)
failure = (failure[0].__name__, str(failure[1]), tb)
- conn = Connection.instance(True)
+ conn = Connection.instance()
publisher = DirectPublisher(connection=conn, msg_id=msg_id)
try:
publisher.send({'result': reply, 'failure': failure})
@@ -319,7 +319,7 @@ def call(context, topic, msg):
self.result = data['result']
wait_msg = WaitMessage()
- conn = Connection.instance(True)
+ conn = Connection.instance()
consumer = DirectConsumer(connection=conn, msg_id=msg_id)
consumer.register_callback(wait_msg)
@@ -346,7 +346,7 @@ def cast(context, topic, msg):
"""Sends a message on a topic without waiting for a response"""
LOG.debug(_("Making asynchronous cast..."))
_pack_context(msg, context)
- conn = Connection.instance(True)
+ conn = Connection.instance()
publisher = TopicPublisher(connection=conn, topic=topic)
publisher.send(msg)
publisher.close()
From e32a0131cfa0d7655545aca50559d9988e62142d Mon Sep 17 00:00:00 2001
From: Salvatore Orlando
Date: 2011年2月11日 13:08:41 +0000
Subject: [PATCH 038/109] Following Rick and Jay's suggestions: - Fixed
LOG.debug for translation - improved vm_utils.VM_Helper.ensure_free_mem
---
nova/virt/xenapi/vm_utils.py | 7 +++----
nova/virt/xenapi/vmops.py | 7 ++++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index c6ac969b9c51..dd5e74a85dcc 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -144,10 +144,9 @@ class VMHelper(HelperBase):
mem = long(instance_type['memory_mb']) * 1024 * 1024
#get free memory from host
host = session.get_xenapi_host()
- host_free_mem = session.get_xenapi().host.compute_free_memory(host)
- if (host_free_mem < mem): - return False - return True + host_free_mem = long(session.get_xenapi().host. + compute_free_memory(host)) + return host_free_mem>= mem
@classmethod
def create_vbd(cls, session, vm_ref, vdi_ref, userdevice, bootable):
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index e3c303d913e7..786768ab5a91 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -66,10 +66,11 @@ class VMOps(object):
if vm is not None:
raise exception.Duplicate(_('Attempted to create'
' non-unique name %s') % instance.name)
- #ensure enough free memory, otherwise don't bother
+ #ensure enough free memory is available
if not VMHelper.ensure_free_mem(self._session, instance):
- LOG.exception(_('instance %s: not enough free memory'),
- instance['name'])
+ name = instance['name']
+ LOG.exception(_('instance %(name)s: not enough free memory')
+ % locals())
db.instance_set_state(context.get_admin_context(),
instance['id'],
power_state.SHUTDOWN)
From c230dba962a3db2a3a8bb502dfb33313f0ef274b Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月11日 11:25:55 -0400
Subject: [PATCH 039/109] rough cut at zone api tests
---
nova/tests/api/openstack/test_zones.py | 74 ++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
create mode 100644 nova/tests/api/openstack/test_zones.py
diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py
new file mode 100644
index 000000000000..8a817bebec3f
--- /dev/null
+++ b/nova/tests/api/openstack/test_zones.py
@@ -0,0 +1,74 @@
+# Copyright 2010 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 unittest
+
+import stubout
+import webob
+
+import nova.api
+from nova.api.openstack import zones
+from nova.tests.api.openstack import fakes
+
+
+class ZonesTest(unittest.TestCase):
+ def setUp(self):
+ self.stubs = stubout.StubOutForTesting()
+ fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthDatabase.data = {}
+ fakes.stub_out_networking(self.stubs)
+ fakes.stub_out_rate_limiting(self.stubs)
+ fakes.stub_out_auth(self.stubs)
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+
+ def test_get_zone_list(self):
+ req = webob.Request.blank('/v1.0/zones')
+ res = req.get_response(fakes.wsgi_app())
+
+ def test_get_zone_by_id(self):
+ req = webob.Request.blank('/v1.0/zones/1')
+ res = req.get_response(fakes.wsgi_app())
+
+ def test_zone_delete(self):
+ req = webob.Request.blank('/v1.0/zones/1')
+ res = req.get_response(fakes.wsgi_app())
+
+ def test_zone_create(self):
+ body = dict(server=dict(api_url='http://blah.zoo', username='bob',
+ password='qwerty'))
+ req = webob.Request.blank('/v1.0/zones')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+
+ self.assertEqual(res.status_int, 200)
+
+ def test_zone_update(self):
+ body = dict(server=dict(api_url='http://blah.zoo', username='zeb',
+ password='sneaky'))
+ req = webob.Request.blank('/v1.0/zones/1')
+ req.method = 'PUT'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+
+ self.assertEqual(res.status_int, 200)
+
+
+if __name__ == '__main__':
+ unittest.main()
From df9bf23ecda1f32fd31ebffc6013e2f60f7fd3fa Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月11日 15:13:05 -0400
Subject: [PATCH 040/109] zone api tests passing
---
nova/api/openstack/__init__.py | 3 +-
nova/api/openstack/zones.py | 1 +
nova/tests/api/openstack/test_zones.py | 81 ++++++++++++++++++++++++--
3 files changed, 78 insertions(+), 7 deletions(-)
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 8aeb69693c9b..33d040ab3d93 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -82,8 +82,7 @@ class APIRouter(wsgi.Router):
server_members['resume'] = 'POST'
mapper.resource("zone", "zones", controller=zones.Controller(),
- collection={'detail': 'GET'},
- collection_name='zones')
+ collection={'detail': 'GET'})
mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'},
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index a12d1cc0c023..e84b38fa9923 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import common
import logging
from nova import flags
diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py
index 8a817bebec3f..8dbdffa41b10 100644
--- a/nova/tests/api/openstack/test_zones.py
+++ b/nova/tests/api/openstack/test_zones.py
@@ -17,12 +17,50 @@ import unittest
import stubout
import webob
+import json
-import nova.api
+import nova.db
+from nova import context
+from nova import flags
from nova.api.openstack import zones
from nova.tests.api.openstack import fakes
+FLAGS = flags.FLAGS
+FLAGS.verbose = True
+
+
+def zone_get(context, zone_id):
+ return dict(id=1, api_url='http://foo.com', username='bob',
+ password='xxx')
+
+
+def zone_create(context, values):
+ zone = dict(id=1)
+ zone.update(values)
+ return zone
+
+
+def zone_update(context, zone_id, values):
+ zone = dict(id=zone_id, api_url='http://foo.com', username='bob',
+ password='xxx')
+ zone.update(values)
+ return zone
+
+
+def zone_delete(context, zone_id):
+ pass
+
+
+def zone_get_all(context):
+ return [
+ dict(id=1, api_url='http://foo.com', username='bob',
+ password='xxx'),
+ dict(id=2, api_url='http://blah.com', username='alice',
+ password='qwerty')
+ ]
+
+
class ZonesTest(unittest.TestCase):
def setUp(self):
self.stubs = stubout.StubOutForTesting()
@@ -32,42 +70,75 @@ class ZonesTest(unittest.TestCase):
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
+ self.allow_admin = FLAGS.allow_admin_api
+ FLAGS.allow_admin_api = True
+
+ self.stubs.Set(nova.db, 'zone_get', zone_get)
+ self.stubs.Set(nova.db, 'zone_get_all', zone_get_all)
+ self.stubs.Set(nova.db, 'zone_update', zone_update)
+ self.stubs.Set(nova.db, 'zone_create', zone_create)
+ self.stubs.Set(nova.db, 'zone_delete', zone_delete)
+
def tearDown(self):
self.stubs.UnsetAll()
+ FLAGS.allow_admin_api = self.allow_admin
def test_get_zone_list(self):
req = webob.Request.blank('/v1.0/zones')
res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(len(res_dict['zones']), 2)
def test_get_zone_by_id(self):
req = webob.Request.blank('/v1.0/zones/1')
res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res_dict['zone']['id'], 1)
+ self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com')
+ self.assertEqual(res_dict['zone']['username'], 'bob')
+ self.assertEqual(res_dict['zone']['password'], 'xxx')
+
+ self.assertEqual(res.status_int, 200)
def test_zone_delete(self):
req = webob.Request.blank('/v1.0/zones/1')
res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+
def test_zone_create(self):
- body = dict(server=dict(api_url='http://blah.zoo', username='bob',
- password='qwerty'))
+ body = dict(zone=dict(api_url='http://blah.zoo', username='fred',
+ password='fubar'))
req = webob.Request.blank('/v1.0/zones')
req.method = 'POST'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
+ self.assertEqual(res_dict['zone']['id'], 1)
+ self.assertEqual(res_dict['zone']['api_url'], 'http://blah.zoo')
+ self.assertEqual(res_dict['zone']['username'], 'fred')
+ self.assertEqual(res_dict['zone']['password'], 'fubar')
def test_zone_update(self):
- body = dict(server=dict(api_url='http://blah.zoo', username='zeb',
- password='sneaky'))
+ body = dict(zone=dict(username='zeb', password='sneaky'))
req = webob.Request.blank('/v1.0/zones/1')
req.method = 'PUT'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
+ self.assertEqual(res_dict['zone']['id'], 1)
+ self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com')
+ self.assertEqual(res_dict['zone']['username'], 'zeb')
+ self.assertEqual(res_dict['zone']['password'], 'sneaky')
if __name__ == '__main__':
From d7042ae6bb37a3bff94d0e90535b92a92d7c94f9 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: 2011年2月11日 11:30:56 -0800
Subject: [PATCH 041/109] joinedload network so describe_instances continues to
work
---
nova/db/sqlalchemy/api.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 80c844635bb7..02855e7a919c 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -719,6 +719,7 @@ def instance_get_all(context):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(deleted=can_read_deleted(context)).\
all()
@@ -729,6 +730,7 @@ def instance_get_all_by_user(context, user_id):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(deleted=can_read_deleted(context)).\
filter_by(user_id=user_id).\
all()
@@ -740,6 +742,7 @@ def instance_get_all_by_host(context, host):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(host=host).\
filter_by(deleted=can_read_deleted(context)).\
all()
@@ -753,6 +756,7 @@ def instance_get_all_by_project(context, project_id):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(project_id=project_id).\
filter_by(deleted=can_read_deleted(context)).\
all()
@@ -766,6 +770,7 @@ def instance_get_all_by_reservation(context, reservation_id):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(reservation_id=reservation_id).\
filter_by(deleted=can_read_deleted(context)).\
all()
@@ -773,6 +778,7 @@ def instance_get_all_by_reservation(context, reservation_id):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
+ options(joinedload_all('fixed_ip.network')).\
filter_by(project_id=context.project_id).\
filter_by(reservation_id=reservation_id).\
filter_by(deleted=False).\
From 7bc6ea8911e95c33b685c8f9b9a0e649f1ebbe93 Mon Sep 17 00:00:00 2001
From: Ricardo Carrillo Cruz
Date: 2011年2月11日 22:02:05 +0100
Subject: [PATCH 042/109] Added LOG line to describe groups function to find
out what's going
---
nova/api/ec2/cloud.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index c80e1168a620..4dc074fd5c01 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -324,6 +324,8 @@ class CloudController(object):
groups = db.security_group_get_by_project(context,
context.project_id)
groups = [self._format_security_group(context, g) for g in groups]
+ LOG.debug(_("Groups after format_security_group: %s"),
+ groups, context=context)
if not group_name is None:
groups = [g for g in groups if g.name in group_name]
From c8521da3539d28dce56a914d89c43c4908e46e57 Mon Sep 17 00:00:00 2001
From: Brian Schott
Date: 2011年2月11日 19:03:45 -0500
Subject: [PATCH 043/109] fixed exceptions import from python migrate
---
nova/db/sqlalchemy/migration.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py
index 2a13c54660ae..acd03ba347d3 100644
--- a/nova/db/sqlalchemy/migration.py
+++ b/nova/db/sqlalchemy/migration.py
@@ -22,7 +22,7 @@ from nova import flags
import sqlalchemy
from migrate.versioning import api as versioning_api
-from migrate.versioning import exceptions as versioning_exceptions
+from migrate import exceptions as versioning_exceptions
FLAGS = flags.FLAGS
From 635f6c9fb2bc3a09fac43832d1e57913ce893714 Mon Sep 17 00:00:00 2001
From: Ricardo Carrillo Cruz
Date: 2011年2月12日 21:32:58 +0100
Subject: [PATCH 044/109] Preliminary fix for issue, need more thorough testing
before pushing to lp
---
nova/api/ec2/cloud.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 4dc074fd5c01..1d89b58cff82 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -324,12 +324,11 @@ class CloudController(object):
groups = db.security_group_get_by_project(context,
context.project_id)
groups = [self._format_security_group(context, g) for g in groups]
- LOG.debug(_("Groups after format_security_group: %s"),
- groups, context=context)
if not group_name is None:
groups = [g for g in groups if g.name in group_name]
- return {'securityGroupInfo': groups}
+ return {'securityGroupInfo':
+ sorted(groups, key=lambda k: k['ownerId'])}
def _format_security_group(self, context, group):
g = {}
From 556e8a585cf3a344f0ecf386e6195e6e61848071 Mon Sep 17 00:00:00 2001
From: termie
Date: 2011年2月12日 13:30:31 -0800
Subject: [PATCH 045/109] fix :returns: and add pep-0257
---
HACKING | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/HACKING b/HACKING
index 4e0dfc8356f1..e58d60e582a4 100644
--- a/HACKING
+++ b/HACKING
@@ -57,8 +57,12 @@ Docstrings
parameters and return types use the Sphinx format. After that an extra
newline then close the quotations.
+ When writing the docstring for a class, an extra line should be placed
+ after the closing quotations. For more in-depth explanations for these
+ decisions see http://www.python.org/dev/peps/pep-0257/
+
:param foo: the foo parameter
:param bar: the bar parameter
- :rtype: the return type
+ :returns: description of the return value
"""
From 6f6a09216458ffdba17d1960bbad723a3e71e7b2 Mon Sep 17 00:00:00 2001
From: Devin Carlen
Date: 2011年2月12日 18:33:49 -0800
Subject: [PATCH 046/109] First attempt to make all image services use similar
schemas
---
nova/api/ec2/cloud.py | 16 +++++++++++++++-
nova/image/s3.py | 22 +++++++++++++++-------
2 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 00d044e95ec2..f1d3ecdd3008 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -836,11 +836,25 @@ class CloudController(object):
self.compute_api.update(context, instance_id=instance_id, **kwargs)
return True
+ def _format_image(self, context, image):
+ i = {}
+ i['imageId'] = image.get('id')
+ i['kernelId'] = image.get('kernel_id')
+ i['ramdiskId'] = image.get('ramdisk_id')
+ i['imageLocation'] = image.get('image_location')
+ i['imageOwnerId'] = image.get('image_owner_id')
+ i['imageState'] = image.get('image_state')
+ i['type'] = image.get('type')
+ i['isPublic'] = image.get('is_public')
+ i['architecture'] = image.get('architecture')
+ return i
+
def describe_images(self, context, image_id=None, **kwargs):
- # Note: image_id is a list!
+ # NOTE: image_id is a list!
images = self.image_service.index(context)
if image_id:
images = filter(lambda x: x['imageId'] in image_id, images)
+ images = [self._format_image(context, i) for i in images]
return {'imagesSet': images}
def deregister_image(self, context, image_id, **kwargs):
diff --git a/nova/image/s3.py b/nova/image/s3.py
index 08a40f191721..30a9e28418cd 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -65,19 +65,27 @@ class S3ImageService(service.BaseImageService):
'image_id': image_id}))
return image_id
- def _fix_image_id(self, images):
- """S3 has imageId but OpenStack wants id"""
- for image in images:
- if 'imageId' in image:
- image['id'] = image['imageId']
- return images
+ def _format_image(self, image):
+ """Convert from S3 format to format defined by BaseImageService."""
+ i = {}
+ i['id'] = image.get('imageId')
+ i['kernel_id'] = image.get('kernelId')
+ i['ramdisk_id'] = image.get('ramdiskId')
+ i['image_location'] = image.get('imageLocation')
+ i['image_owner_id'] = image.get('imageOwnerId')
+ i['image_state'] = image.get('imageState')
+ i['type'] = image.get('type')
+ i['is_public'] = image.get('isPublic')
+ i['architecture'] = image.get('architecture')
+ return i
def index(self, context):
"""Return a list of all images that a user can see."""
response = self._conn(context).make_request(
method='GET',
bucket='_images')
- return self._fix_image_id(json.loads(response.read()))
+ images = json.loads(response.read())
+ return [self._format_image(i) for i in images]
def show(self, context, image_id):
"""return a image object if the context has permissions"""
From 07bff0f397b3b6463532f709daeb7a36ed4ad5b1 Mon Sep 17 00:00:00 2001
From: Devin Carlen
Date: 2011年2月12日 20:58:22 -0800
Subject: [PATCH 047/109] Made a few tweaks to format of S3 service
implementation
---
nova/api/ec2/cloud.py | 8 ++++----
nova/image/s3.py | 7 ++++---
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index b1650358964a..cc41ed4ae0ad 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -844,9 +844,9 @@ class CloudController(object):
i['imageId'] = image.get('id')
i['kernelId'] = image.get('kernel_id')
i['ramdiskId'] = image.get('ramdisk_id')
- i['imageLocation'] = image.get('image_location')
- i['imageOwnerId'] = image.get('image_owner_id')
- i['imageState'] = image.get('image_state')
+ i['imageOwnerId'] = image.get('owner_id')
+ i['imageLocation'] = image.get('location')
+ i['imageState'] = image.get('status')
i['type'] = image.get('type')
i['isPublic'] = image.get('is_public')
i['architecture'] = image.get('architecture')
@@ -856,7 +856,7 @@ class CloudController(object):
# NOTE: image_id is a list!
images = self.image_service.index(context)
if image_id:
- images = filter(lambda x: x['imageId'] in image_id, images)
+ images = filter(lambda x: x['id'] in image_id, images)
images = [self._format_image(context, i) for i in images]
return {'imagesSet': images}
diff --git a/nova/image/s3.py b/nova/image/s3.py
index 30a9e28418cd..ee6b7f0d0161 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -69,11 +69,12 @@ class S3ImageService(service.BaseImageService):
"""Convert from S3 format to format defined by BaseImageService."""
i = {}
i['id'] = image.get('imageId')
+ i['name'] = image.get('imageId')
i['kernel_id'] = image.get('kernelId')
i['ramdisk_id'] = image.get('ramdiskId')
- i['image_location'] = image.get('imageLocation')
- i['image_owner_id'] = image.get('imageOwnerId')
- i['image_state'] = image.get('imageState')
+ i['location'] = image.get('imageLocation')
+ i['owner_id'] = image.get('imageOwnerId')
+ i['status'] = image.get('imageState')
i['type'] = image.get('type')
i['is_public'] = image.get('isPublic')
i['architecture'] = image.get('architecture')
From abe00772e1b8eef361ff2ce614a4679603438228 Mon Sep 17 00:00:00 2001
From: Ricardo Carrillo Cruz
Date: 2011年2月13日 19:34:20 +0100
Subject: [PATCH 048/109] now sorting by project, then by group
---
nova/api/ec2/cloud.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 1d89b58cff82..41a7ac706820 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -328,7 +328,7 @@ class CloudController(object):
groups = [g for g in groups if g.name in group_name]
return {'securityGroupInfo':
- sorted(groups, key=lambda k: k['ownerId'])}
+ sorted(groups, key=lambda k: (k['ownerId'], k['groupName']))}
def _format_security_group(self, context, group):
g = {}
From 9f3d269c9b5b610846acbf4ba2bf6719577b199a Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: 2011年2月13日 10:45:20 -0800
Subject: [PATCH 049/109] fix typo in auth checking for
describe_availability_zones
---
nova/api/ec2/__init__.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index ddcdc673c794..1a06b3f010c9 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -21,7 +21,6 @@ Starting point for routing EC2 requests.
"""
import datetime
-import routes
import webob
import webob.dec
import webob.exc
@@ -233,7 +232,7 @@ class Authorizer(wsgi.Middleware):
super(Authorizer, self).__init__(application)
self.action_roles = {
'CloudController': {
- 'DescribeAvailabilityzones': ['all'],
+ 'DescribeAvailabilityZones': ['all'],
'DescribeRegions': ['all'],
'DescribeSnapshots': ['all'],
'DescribeKeyPairs': ['all'],
From 27139d33ec027bae4b247c1651bfe635a32f5ebf Mon Sep 17 00:00:00 2001
From: Devin Carlen
Date: 2011年2月13日 11:55:50 -0800
Subject: [PATCH 050/109] Added missing doc string and made a few style tweaks
---
nova/api/ec2/cloud.py | 1 +
nova/image/s3.py | 33 +++++++++++++++++----------------
2 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index cc41ed4ae0ad..47cd8bf37340 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -840,6 +840,7 @@ class CloudController(object):
return True
def _format_image(self, context, image):
+ """Convert from format defined by BaseImageService to S3 format."""
i = {}
i['imageId'] = image.get('id')
i['kernelId'] = image.get('kernel_id')
diff --git a/nova/image/s3.py b/nova/image/s3.py
index ee6b7f0d0161..71304cdd66f3 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -36,6 +36,22 @@ from nova.image import service
FLAGS = flags.FLAGS
+def map_s3_to_base(image):
+ """Convert from S3 format to format defined by BaseImageService."""
+ i = {}
+ i['id'] = image.get('imageId')
+ i['name'] = image.get('imageId')
+ i['kernel_id'] = image.get('kernelId')
+ i['ramdisk_id'] = image.get('ramdiskId')
+ i['location'] = image.get('imageLocation')
+ i['owner_id'] = image.get('imageOwnerId')
+ i['status'] = image.get('imageState')
+ i['type'] = image.get('type')
+ i['is_public'] = image.get('isPublic')
+ i['architecture'] = image.get('architecture')
+ return i
+
+
class S3ImageService(service.BaseImageService):
def modify(self, context, image_id, operation):
@@ -65,28 +81,13 @@ class S3ImageService(service.BaseImageService):
'image_id': image_id}))
return image_id
- def _format_image(self, image):
- """Convert from S3 format to format defined by BaseImageService."""
- i = {}
- i['id'] = image.get('imageId')
- i['name'] = image.get('imageId')
- i['kernel_id'] = image.get('kernelId')
- i['ramdisk_id'] = image.get('ramdiskId')
- i['location'] = image.get('imageLocation')
- i['owner_id'] = image.get('imageOwnerId')
- i['status'] = image.get('imageState')
- i['type'] = image.get('type')
- i['is_public'] = image.get('isPublic')
- i['architecture'] = image.get('architecture')
- return i
-
def index(self, context):
"""Return a list of all images that a user can see."""
response = self._conn(context).make_request(
method='GET',
bucket='_images')
images = json.loads(response.read())
- return [self._format_image(i) for i in images]
+ return [map_s3_to_base(i) for i in images]
def show(self, context, image_id):
"""return a image object if the context has permissions"""
From 7713cb99635f3a06e298d4a504dc1249e5bf3232 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: 2011年2月13日 15:44:41 -0800
Subject: [PATCH 051/109] return a list instead of a generator from
describe_groups
---
nova/api/ec2/cloud.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 41a7ac706820..5d387d45d894 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -328,7 +328,8 @@ class CloudController(object):
groups = [g for g in groups if g.name in group_name]
return {'securityGroupInfo':
- sorted(groups, key=lambda k: (k['ownerId'], k['groupName']))}
+ list(sorted(groups,
+ key=lambda k: (k['ownerId'], k['groupName'])))}
def _format_security_group(self, context, group):
g = {}
From 17cca3c90f203a023d921e7b01a667e68ef695b5 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: 2011年2月13日 21:48:14 -0800
Subject: [PATCH 052/109] re-add input_chain because it got deleted at some
point
---
nova/network/linux_net.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index ed37e8ba7e6f..c1cbff7d8aee 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -54,6 +54,8 @@ flags.DEFINE_string('routing_source_ip', '$my_ip',
'Public IP of network host')
flags.DEFINE_bool('use_nova_chains', False,
'use the nova_ routing chains instead of default')
+flags.DEFINE_string('input_chain', 'INPUT',
+ 'chain to add nova_input to')
flags.DEFINE_string('dns_server', None,
'if set, uses specific dns server for dnsmasq')
From 64a2a487d8ad524c0e948545a2318cebfefb36fe Mon Sep 17 00:00:00 2001
From: Vasiliy Shlykov
Date: 2011年2月14日 16:02:58 +0300
Subject: [PATCH 053/109] Fixed tables creation order and added clearing db
after errors.
---
.../sqlalchemy/migrate_repo/versions/001_austin.py | 14 ++++++++------
.../sqlalchemy/migrate_repo/versions/002_bexar.py | 7 +++++--
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
index 366944591e8d..9e7ab3554295 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
@@ -508,17 +508,19 @@ def upgrade(migrate_engine):
# bind migrate_engine to your metadata
meta.bind = migrate_engine
- for table in (auth_tokens, export_devices, fixed_ips, floating_ips,
- instances, key_pairs, networks,
- projects, quotas, security_groups, security_group_inst_assoc,
- security_group_rules, services, users,
- user_project_association, user_project_role_association,
- user_role_association, volumes):
+ tables = [auth_tokens,
+ instances, key_pairs, networks, fixed_ips, floating_ips,
+ quotas, security_groups, security_group_inst_assoc,
+ security_group_rules, services, users, projects,
+ user_project_association, user_project_role_association,
+ user_role_association, volumes, export_devices]
+ for table in tables:
try:
table.create()
except Exception:
logging.info(repr(table))
logging.exception('Exception while creating table')
+ meta.drop_all(tables=tables)
raise
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
index 699b837f8b20..413536a5929f 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
@@ -209,13 +209,16 @@ 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 (certificates, consoles, console_pools, instance_actions,
- iscsi_targets):
+
+ tables = [certificates, console_pools, consoles, instance_actions,
+ iscsi_targets]
+ for table in tables:
try:
table.create()
except Exception:
logging.info(repr(table))
logging.exception('Exception while creating table')
+ meta.drop_all(tables=tables)
raise
auth_tokens.c.user_id.alter(type=String(length=255,
From 9c0862b5f84cdb09b7ab0aafca669d30f261a666 Mon Sep 17 00:00:00 2001
From: Trey Morris Date: 2011年2月14日 10:21:16 -0600
Subject: [PATCH 054/109] support for multiple IPs per network
---
nova/virt/xenapi/vmops.py | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 575e53f80ff0..db05a24ff98e 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -96,22 +96,36 @@ class VMOps(object):
# write network info
admin_context = context.get_admin_context()
- #network = db.network_get_by_instance(admin_context,
- # instance['id'])
+ # TODO(tr3buchet) - remove comment in multi-nic
+ # I've decided to go ahead and consider multiple IPs and networks
+ # at this stage even though they aren't implemented because these will
+ # be needed for multi-nic and there was no sense writing it for single
+ # network/single IP and then having to turn around and re-write it
+ IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id'])
for network in db.network_get_all_by_instance(admin_context,
instance['id']):
+ network_IPs = [ip for ip in IPs if ip.network_id == network.id]
+
+ def ip_dict(ip):
+ return {'netmask': network['netmask'],
+ 'enabled': '1',
+ 'ip': ip.address}
+
mac_id = instance.mac_address.replace(':', '')
location = 'vm-data/networking/%s' % mac_id
mapping = {'label': network['label'],
'gateway': network['gateway'],
'mac': instance.mac_address,
'dns': [network['dns']],
- 'ips': [{'netmask': network['netmask'],
- 'enabled': '1',
- 'ip': '192.168.3.3'}]} # <===== CHANGE!!!! + 'ips': [ip_dict(ip) for ip in network_IPs]} self.write_to_param_xenstore(vm_ref, {location: mapping}) + # TODO(tr3buchet) - remove comment in multi-nic + # this bit here about creating the vifs will be updated + # in multi-nic to handle multiple IPs on the same network + # and multiple networks + # for now it works as there is only one of each bridge = network['bridge'] network_ref = \ NetworkHelper.find_network_with_bridge(self._session, bridge) From ee26d0827b7ad3e4d7869614835fe58abe32dfc8 Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz
Date: 2011年2月14日 17:43:39 +0100
Subject: [PATCH 055/109] Got rid of BadParameter, just using standard python
ValueError
---
nova/network/manager.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 8eb9f041b17c..d911844a16ea 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -505,6 +505,12 @@ class VlanManager(NetworkManager):
def create_networks(self, context, cidr, num_networks, network_size,
cidr_v6, vlan_start, vpn_start):
"""Create networks based on parameters."""
+ # Check that num_networks + vlan_start is not> 4094, fixes lp708025
+ if num_networks + vlan_start> 4094:
+ raise ValueError(_('The sum between the number of networks and'
+ ' the vlan start cannot be greater'
+ ' than 4094'))
+
fixed_net = IPy.IP(cidr)
fixed_net_v6 = IPy.IP(cidr_v6)
network_size_v6 = 1 << 64 From 3f96e6dbf12533355aa6722eeb498814df076aea Mon Sep 17 00:00:00 2001 From: Trey Morris Date: 2011年2月14日 12:32:33 -0600
Subject: [PATCH 056/109] added call to reset_network from openstack api down
to vmops
---
nova/api/openstack/servers.py | 14 ++++++++++++++
nova/compute/api.py | 9 ++++++++-
nova/compute/manager.py | 12 ++++++++++++
nova/virt/xenapi_conn.py | 4 ++++
4 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 8cbcebed2ec6..c604bd215557 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -242,6 +242,20 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
+ def reset_network(self, req, id):
+ """
+ admin only operation which resets networking on an instance
+
+ """
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.reset_network(context, id)
+ except:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::reset_network %s"), readable)
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ return exc.HTTPAccepted()
+
def pause(self, req, id):
""" Permit Admins to Pause the server. """
ctxt = req.environ['nova.context']
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 6a3fe08b6699..43332ed27db9 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1,4 +1,4 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# vim: tabstop=5 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
@@ -463,6 +463,13 @@ class API(base.Base):
instance = self.get(context, instance_id)
return instance['locked']
+ def reset_network(self, context, instance_id):
+ """
+ resets networking on the instance
+
+ """
+ self._cast_compute_message('reset_network', context, instance_id)
+
def attach_volume(self, context, instance_id, volume_id, device):
if not re.match("^/dev/[a-z]d[a-z]+$", device):
raise exception.ApiError(_("Invalid device specified: %s. "
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 6f09ce674a51..b03f58693986 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -494,6 +494,18 @@ class ComputeManager(manager.Manager):
instance_ref = self.db.instance_get(context, instance_id)
return instance_ref['locked']
+ @checks_instance_lock
+ def reset_network(self, context, instance_id):
+ """
+ resets the networking on the instance
+
+ """
+ context = context.elevated()
+ instance_ref = self.db.instance_get(context, instance_id)
+ LOG.debug(_('instance %s: reset network'), instance_id,
+ context=context)
+ self.driver.reset_network(instance_ref)
+
@exception.wrap_exception
def get_console_output(self, context, instance_id):
"""Send the console output for an instance."""
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 927f5905ba6f..4e5442aa62f1 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -188,6 +188,10 @@ class XenAPIConnection(object):
"""resume the specified instance"""
self._vmops.resume(instance, callback)
+ def reset_network(self, instance):
+ """reset networking for specified instance"""
+ self._vmops.reset_network(instance)
+
def get_info(self, instance_id):
"""Return data about VM instance"""
return self._vmops.get_info(instance_id)
From a7f796c0a3dbccb74ba3fe51e3885c716bc88592 Mon Sep 17 00:00:00 2001
From: Anthony Young
Date: 2011年2月14日 10:43:22 -0800
Subject: [PATCH 057/109] fix for bug #716847 - if a volume has not been
assigned to a host, then delete from db and skip rpc
---
bin/nova-manage | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/bin/nova-manage b/bin/nova-manage
index 7835ca551ac5..e4c0684c4ba4 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -579,6 +579,13 @@ class VolumeCommands(object):
ctxt = context.get_admin_context()
volume = db.volume_get(ctxt, param2id(volume_id))
host = volume['host']
+
+ if not host:
+ print "Volume not yet assigned to host."
+ print "Deleting volume from database and skipping rpc."
+ db.volume_destroy(ctxt, param2id(volume_id))
+ return
+
if volume['status'] == 'in-use':
print "Volume is in-use."
print "Detach volume from instance and then try again."
From ee4cba7779daa5b2e7415fb69cabc698b7dd60da Mon Sep 17 00:00:00 2001
From: Trey Morris Date: 2011年2月14日 12:59:46 -0600
Subject: [PATCH 058/109] corrected model for table lookup
---
nova/db/sqlalchemy/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index f20f4e2667a7..827f81ae286e 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -609,7 +609,7 @@ def fixed_ip_get_instance(context, address):
@require_context
def fixed_ip_get_all_by_instance(context, instance_id):
session = get_session()
- rv = session.query(models.Network.fixed_ips).\
+ rv = session.query(models.FixedIp).\
filter_by(instance_id=instance_id).\
filter_by(deleted=False)
if not rv:
From 89ca05c457cb951e91060359493e5f830fe5eeda Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月14日 21:06:16 +0100
Subject: [PATCH 059/109] Use eventlet.green.subprocess instead of standard
subprocess
Eventlet's monkey patching causes the os.wait call in the standard
subprocess module to be non-blocking. This means that if it happens
to call self.wait on a Popen object that hasn't completely terminated
it'll be left as a zombie and its fd's are also leaked.
---
nova/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/utils.py b/nova/utils.py
index 8d7ff1f641e2..ba71ebf392e8 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -25,7 +25,6 @@ import inspect
import json
import os
import random
-import subprocess
import socket
import struct
import sys
@@ -36,6 +35,7 @@ import netaddr
from eventlet import event
from eventlet import greenthread
+from eventlet.green import subprocess
from nova import exception
from nova.exception import ProcessExecutionError
From 96d0edff2348040362b77491892e525217a17562 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月14日 23:19:15 +0100
Subject: [PATCH 060/109] Resurrect logdir option.
---
nova/flags.py | 2 ++
nova/log.py | 11 +++++++++--
nova/twistd.py | 2 --
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/nova/flags.py b/nova/flags.py
index 3ba3fe6faa1e..f64a62da9ca4 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -282,6 +282,8 @@ DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger')
DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'),
"Top-level directory for maintaining nova's state")
+DEFINE_string('logdir', None, 'output to a per-service log file in named '
+ 'directory')
DEFINE_string('sql_connection',
'sqlite:///$state_path/nova.sqlite',
diff --git a/nova/log.py b/nova/log.py
index b541488bdcdd..a5b4828d5c82 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -28,9 +28,11 @@ It also allows setting of formatting information through flags.
import cStringIO
+import inspect
import json
import logging
import logging.handlers
+import os
import sys
import traceback
@@ -123,8 +125,13 @@ def basicConfig():
syslog = SysLogHandler(address='/dev/log')
syslog.setFormatter(_formatter)
logging.root.addHandler(syslog)
- if FLAGS.logfile:
- logfile = FileHandler(FLAGS.logfile)
+ if FLAGS.logfile or FLAGS.logdir:
+ if FLAGS.logfile:
+ logfile = FLAGS.logfile
+ else:
+ binary = os.path.basename(inspect.stack()[-1][1])
+ logpath = '%s.log' % (os.path.join(FLAGS.logdir, binary),)
+ logfile = FileHandler(logpath)
logfile.setFormatter(_formatter)
logging.root.addHandler(logfile)
diff --git a/nova/twistd.py b/nova/twistd.py
index 6390a814452f..60ff7879a0fa 100644
--- a/nova/twistd.py
+++ b/nova/twistd.py
@@ -43,8 +43,6 @@ else:
FLAGS = flags.FLAGS
-flags.DEFINE_string('logdir', None, 'directory to keep log files in '
- '(will be prepended to $logfile)')
class TwistdServerOptions(ServerOptions):
From 2816ca39281bf7c1994791d969bc63d22c86f911 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月14日 23:27:31 +0100
Subject: [PATCH 061/109] Use RotatingFileHandler instead of FileHandler.
---
nova/log.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/log.py b/nova/log.py
index b541488bdcdd..413dd946b455 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -92,7 +92,7 @@ critical = logging.critical
log = logging.log
# handlers
StreamHandler = logging.StreamHandler
-FileHandler = logging.FileHandler
+FileHandler = logging.RotatingFileHandler
# logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler.
SysLogHandler = logging.handlers.SysLogHandler
From 6147a606cbe6b7e764865d2471d86f503437051b Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月14日 14:52:58 -0800
Subject: [PATCH 062/109] fixed template and added migration
---
nova/auth/novarc.template | 6 +-
.../migrate_repo/versions/003_cactus.py | 62 +++++++++++++++++++
2 files changed, 65 insertions(+), 3 deletions(-)
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
diff --git a/nova/auth/novarc.template b/nova/auth/novarc.template
index 702df3bb060b..cda2ecc28b68 100644
--- a/nova/auth/novarc.template
+++ b/nova/auth/novarc.template
@@ -10,6 +10,6 @@ export NOVA_CERT=${NOVA_KEY_DIR}/%(nova)s
export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set
alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}"
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}"
-export NOVA_TOOLS_API_KEY="%(access)s"
-export NOVA_TOOLS_USERNAME="%(user)s"
-export NOVA_TOOLS_URL="%(os)s"
+export NOVA_API_KEY="%(access)s"
+export NOVA_USERNAME="%(user)s"
+export NOVA_URL="%(os)s"
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
new file mode 100644
index 000000000000..eb3287077598
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
@@ -0,0 +1,62 @@
+# Copyright 2010 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.
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+
+#
+# New Tables
+#
+child_zones = Table('child_zones', 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('api_url',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('username',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('password',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ )
+
+
+#
+# Tables to alter
+#
+
+# (none currently)
+
+
+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 (child_zones, ):
+ try:
+ table.create()
+ except Exception:
+ logging.info(repr(table))
From 1da5dcc0644a13cfb99852f3438649f710feb2bc Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月14日 14:54:04 -0800
Subject: [PATCH 063/109] removed debugging
---
nova/api/openstack/auth.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index a383ef086843..473071738ea7 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -79,7 +79,6 @@ class AuthMiddleware(wsgi.Middleware):
except KeyError:
return faults.Fault(webob.exc.HTTPUnauthorized())
- logging.debug("**** USERNAME %s, PASSWORD %s" % (username, key))
token, user = self._authorize_user(username, key, req)
if user and token:
res = webob.Response()
From e65291cf34894322bd0f3f6661907e48e7a6a0b5 Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月14日 20:11:29 -0400
Subject: [PATCH 064/109] fixed nova-combined debug hack and renamed ChildZone
to Zone
---
bin/nova-combined | 4 ++--
nova/db/api.py | 10 +++++-----
nova/db/sqlalchemy/api.py | 10 +++++-----
nova/db/sqlalchemy/migration.py | 2 +-
nova/db/sqlalchemy/models.py | 6 +++---
5 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/bin/nova-combined b/bin/nova-combined
index a0f552d643fa..913c866bf28e 100755
--- a/bin/nova-combined
+++ b/bin/nova-combined
@@ -53,11 +53,11 @@ if __name__ == '__main__':
compute = service.Service.create(binary='nova-compute')
network = service.Service.create(binary='nova-network')
- #volume = service.Service.create(binary='nova-volume')
+ volume = service.Service.create(binary='nova-volume')
scheduler = service.Service.create(binary='nova-scheduler')
#objectstore = service.Service.create(binary='nova-objectstore')
- service.serve(compute, network, scheduler)
+ service.serve(compute, network, volume, scheduler)
apps = []
paste_config_file = wsgi.paste_config_file('nova-api.conf')
diff --git a/nova/db/api.py b/nova/db/api.py
index fa73d86ad3d6..939f1a06911d 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -986,25 +986,25 @@ def console_get(context, console_id, instance_id=None):
def zone_create(context, values):
- """Create a new ChildZone entry in this Zone."""
+ """Create a new child Zone entry."""
return IMPL.zone_create(context, values)
def zone_update(context, zone_id, values):
- """Update a ChildZone entry in this Zone."""
+ """Update a child Zone entry."""
return IMPL.zone_update(context, values)
def zone_delete(context, zone_id):
- """Delete a ChildZone."""
+ """Delete a child Zone."""
return IMPL.zone_delete(context, zone_id)
def zone_get(context, zone_id):
- """Get a specific ChildZone."""
+ """Get a specific child Zone."""
return IMPL.zone_get(context, zone_id)
def zone_get_all(context):
- """Get all ChildZone's."""
+ """Get all child Zones."""
return IMPL.zone_get_all(context)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index b3320c8199ba..abd65b67b11f 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -2021,7 +2021,7 @@ def console_get(context, console_id, instance_id=None):
@require_admin_context
def zone_create(context, values):
- zone = models.ChildZone()
+ zone = models.Zone()
zone.update(values)
zone.save()
return zone
@@ -2029,7 +2029,7 @@ def zone_create(context, values):
@require_admin_context
def zone_update(context, zone_id, values):
- zone = session.query(models.ChildZone).filter_by(id=zone_id).first()
+ zone = session.query(models.Zone).filter_by(id=zone_id).first()
if not zone:
raise exception.NotFound(_("No zone with id %(zone_id)s") % locals())
zone.update(values)
@@ -2041,14 +2041,14 @@ def zone_update(context, zone_id, values):
def zone_delete(context, zone_id):
session = get_session()
with session.begin():
- session.execute('delete from child_zones '
+ session.execute('delete from zones '
'where id=:id', {'id': zone_id})
@require_admin_context
def zone_get(context, zone_id):
session = get_session()
- result = session.query(models.ChildZone).filter_by(id=zone_id).first()
+ result = session.query(models.Zone).filter_by(id=zone_id).first()
if not result:
raise exception.NotFound(_("No zone with id %(zone_id)s") % locals())
return result
@@ -2057,4 +2057,4 @@ def zone_get(context, zone_id):
@require_admin_context
def zone_get_all(context):
session = get_session()
- return session.query(models.ChildZone).all()
+ return session.query(models.Zone).all()
diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py
index 194ecc6276dd..1d9c041f5d29 100644
--- a/nova/db/sqlalchemy/migration.py
+++ b/nova/db/sqlalchemy/migration.py
@@ -45,7 +45,7 @@ def db_version():
engine = sqlalchemy.create_engine(FLAGS.sql_connection, echo=False)
meta.reflect(bind=engine)
try:
- for table in ('auth_tokens', 'child_zones', 'export_devices',
+ for table in ('auth_tokens', 'zones', 'export_devices',
'fixed_ips', 'floating_ips', 'instances',
'key_pairs', 'networks', 'projects', 'quotas',
'security_group_instance_association',
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 071d57fcad7e..4930c7a2a18b 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -535,9 +535,9 @@ class Console(BASE, NovaBase):
pool = relationship(ConsolePool, backref=backref('consoles'))
-class ChildZone(BASE, NovaBase):
+class Zone(BASE, NovaBase):
"""Represents a child zone of this zone."""
- __tablename__ = 'child_zones'
+ __tablename__ = 'zones'
id = Column(Integer, primary_key=True)
api_url = Column(String(255))
username = Column(String(255))
@@ -556,7 +556,7 @@ def register_models():
Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp,
Network, SecurityGroup, SecurityGroupIngressRule,
SecurityGroupInstanceAssociation, AuthToken, User,
- Project, Certificate, ConsolePool, Console, ChildZone)
+ Project, Certificate, ConsolePool, Console, Zone)
engine = create_engine(FLAGS.sql_connection, echo=False)
for model in models:
model.metadata.create_all(engine)
From f1e536fb296c927a9fc953b1dfe24b9060a0387a Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: 2011年2月14日 19:51:51 -0500
Subject: [PATCH 065/109] Updates to that S3ImageService kernel_id and
ramdisk_id mappings work with EC2 API.
---
nova/api/ec2/cloud.py | 3 +++
nova/compute/api.py | 4 ++--
nova/image/s3.py | 2 +-
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 16a3a4521f72..6919cd8d2ecf 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -883,6 +883,9 @@ class CloudController(object):
% attribute)
try:
image = self.image_service.show(context, image_id)
+ image = self._format_image(context,
+ self.image_service.show(context,
+ image_id))
except IndexError:
raise exception.ApiError(_('invalid id: %s') % image_id)
result = {'image_id': image_id, 'launchPermission': []}
diff --git a/nova/compute/api.py b/nova/compute/api.py
index ac02dbcfa882..740dd393567d 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -103,9 +103,9 @@ class API(base.Base):
if not is_vpn:
image = self.image_service.show(context, image_id)
if kernel_id is None:
- kernel_id = image.get('kernelId', None)
+ kernel_id = image.get('kernel_id', None)
if ramdisk_id is None:
- ramdisk_id = image.get('ramdiskId', None)
+ ramdisk_id = image.get('ramdisk_id', None)
# No kernel and ramdisk for raw images
if kernel_id == str(FLAGS.null_kernel):
kernel_id = None
diff --git a/nova/image/s3.py b/nova/image/s3.py
index 71304cdd66f3..14135a1ee432 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -94,7 +94,7 @@ class S3ImageService(service.BaseImageService):
if FLAGS.connection_type == 'fake':
return {'imageId': 'bar'}
result = self.index(context)
- result = [i for i in result if i['imageId'] == image_id]
+ result = [i for i in result if i['id'] == image_id]
if not result:
raise exception.NotFound(_('Image %s could not be found')
% image_id)
From c15289a63c90218a573d5e75833985ec2ad8691e Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月14日 23:02:26 -0400
Subject: [PATCH 066/109] better filtering
---
nova/api/openstack/zones.py | 13 ++++++++-----
nova/tests/api/openstack/test_zones.py | 10 +++-------
2 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index 2c93c0c7beae..830464ffd63a 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -33,6 +33,10 @@ def _filter_keys(item, keys):
return dict((k, v) for k, v in item.iteritems() if k in keys)
+def _scrub_zone(zone):
+ return _filter_keys(zone, ('id', 'api_url'))
+
+
class Controller(wsgi.Controller):
_serialization_metadata = {
@@ -44,7 +48,7 @@ class Controller(wsgi.Controller):
"""Return all zones in brief"""
items = db.zone_get_all(req.environ['nova.context'])
items = common.limited(items, req)
- items = [_filter_keys(item, ('id', 'api_url')) for item in items]
+ items = [_scrub_zone(item) for item in items]
return dict(zones=items)
def detail(self, req):
@@ -55,8 +59,7 @@ class Controller(wsgi.Controller):
"""Return data about the given zone id"""
zone_id = int(id)
zone = db.zone_get(req.environ['nova.context'], zone_id)
- zone = _filter_keys(zone, ('id', 'api_url'))
- return dict(zone=zone)
+ return dict(zone=_scrub_zone(zone))
def delete(self, req, id):
zone_id = int(id)
@@ -67,11 +70,11 @@ class Controller(wsgi.Controller):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone = db.zone_create(context, env["zone"])
- return dict(zone=zone)
+ return dict(zone=_scrub_zone(zone))
def update(self, req, id):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone_id = int(id)
zone = db.zone_update(context, zone_id, env["zone"])
- return dict(zone=zone)
+ return dict(zone=_scrub_zone(zone))
diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py
index 8dbdffa41b10..5542a1cf3e2d 100644
--- a/nova/tests/api/openstack/test_zones.py
+++ b/nova/tests/api/openstack/test_zones.py
@@ -98,9 +98,7 @@ class ZonesTest(unittest.TestCase):
self.assertEqual(res_dict['zone']['id'], 1)
self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com')
- self.assertEqual(res_dict['zone']['username'], 'bob')
- self.assertEqual(res_dict['zone']['password'], 'xxx')
-
+ self.assertFalse('password' in res_dict['zone'])
self.assertEqual(res.status_int, 200)
def test_zone_delete(self):
@@ -122,8 +120,7 @@ class ZonesTest(unittest.TestCase):
self.assertEqual(res.status_int, 200)
self.assertEqual(res_dict['zone']['id'], 1)
self.assertEqual(res_dict['zone']['api_url'], 'http://blah.zoo')
- self.assertEqual(res_dict['zone']['username'], 'fred')
- self.assertEqual(res_dict['zone']['password'], 'fubar')
+ self.assertFalse('username' in res_dict['zone'])
def test_zone_update(self):
body = dict(zone=dict(username='zeb', password='sneaky'))
@@ -137,8 +134,7 @@ class ZonesTest(unittest.TestCase):
self.assertEqual(res.status_int, 200)
self.assertEqual(res_dict['zone']['id'], 1)
self.assertEqual(res_dict['zone']['api_url'], 'http://foo.com')
- self.assertEqual(res_dict['zone']['username'], 'zeb')
- self.assertEqual(res_dict['zone']['password'], 'sneaky')
+ self.assertFalse('username' in res_dict['zone'])
if __name__ == '__main__':
From 9ab97a9b0860f912be2a085327eea6b9a7fa7674 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月15日 12:33:31 +0100
Subject: [PATCH 067/109] Break out of the "for group in rv" loop in security
group unit tests so that we are use we are dealing with the correct group.
---
nova/tests/test_api.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index 2569e262b760..efd6d94a8e05 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -258,6 +258,7 @@ class ApiEc2TestCase(test.TestCase):
self.assertEquals(int(group.rules[0].to_port), 81)
self.assertEquals(len(group.rules[0].grants), 1)
self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0')
+ break
self.expect_http()
self.mox.ReplayAll()
@@ -324,6 +325,7 @@ class ApiEc2TestCase(test.TestCase):
self.assertEquals(int(group.rules[0].to_port), 81)
self.assertEquals(len(group.rules[0].grants), 1)
self.assertEquals(str(group.rules[0].grants[0]), '::/0')
+ break
self.expect_http()
self.mox.ReplayAll()
From bf8d9d3adfcb5e5cd97ae0d7451e6253892622b1 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月15日 13:18:27 +0100
Subject: [PATCH 068/109] Beautify it a little bit, thanks to dabo.
---
nova/tests/test_api.py | 37 +++++++++++++++----------------------
1 file changed, 15 insertions(+), 22 deletions(-)
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index efd6d94a8e05..fa27825cdfa9 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -248,17 +248,14 @@ class ApiEc2TestCase(test.TestCase):
self.mox.ReplayAll()
rv = self.ec2.get_all_security_groups()
- # I don't bother checkng that we actually find it here,
- # because the create/delete unit test further up should
- # be good enough for that.
- for group in rv:
- if group.name == security_group_name:
- self.assertEquals(len(group.rules), 1)
- self.assertEquals(int(group.rules[0].from_port), 80)
- self.assertEquals(int(group.rules[0].to_port), 81)
- self.assertEquals(len(group.rules[0].grants), 1)
- self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0')
- break
+
+ group = [grp for grp in rv if grp.name == security_group_name][0]
+
+ self.assertEquals(len(group.rules), 1)
+ self.assertEquals(int(group.rules[0].from_port), 80)
+ self.assertEquals(int(group.rules[0].to_port), 81)
+ self.assertEquals(len(group.rules[0].grants), 1)
+ self.assertEquals(str(group.rules[0].grants[0]), '0.0.0.0/0')
self.expect_http()
self.mox.ReplayAll()
@@ -315,17 +312,13 @@ class ApiEc2TestCase(test.TestCase):
self.mox.ReplayAll()
rv = self.ec2.get_all_security_groups()
- # I don't bother checkng that we actually find it here,
- # because the create/delete unit test further up should
- # be good enough for that.
- for group in rv:
- if group.name == security_group_name:
- self.assertEquals(len(group.rules), 1)
- self.assertEquals(int(group.rules[0].from_port), 80)
- self.assertEquals(int(group.rules[0].to_port), 81)
- self.assertEquals(len(group.rules[0].grants), 1)
- self.assertEquals(str(group.rules[0].grants[0]), '::/0')
- break
+
+ group = [grp for grp in rv if grp.name == security_group_name][0]
+ self.assertEquals(len(group.rules), 1)
+ self.assertEquals(int(group.rules[0].from_port), 80)
+ self.assertEquals(int(group.rules[0].to_port), 81)
+ self.assertEquals(len(group.rules[0].grants), 1)
+ self.assertEquals(str(group.rules[0].grants[0]), '::/0')
self.expect_http()
self.mox.ReplayAll()
From dfcf07192cf40d0451c7dfa3802994e4cef8d116 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月15日 14:02:19 +0100
Subject: [PATCH 069/109] =?UTF-8?q?Na=C3=AFve=20attempt=20at=20threading?=
=?UTF-8?q?=20rpc=20requests.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
nova/rpc.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/nova/rpc.py b/nova/rpc.py
index 2b1f7298b9c5..efe6164add38 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -29,6 +29,7 @@ import uuid
from carrot import connection as carrot_connection
from carrot import messaging
+from eventlet import greenpool
from eventlet import greenthread
from nova import context
@@ -155,11 +156,15 @@ class AdapterConsumer(TopicConsumer):
def __init__(self, connection=None, topic="broadcast", proxy=None):
LOG.debug(_('Initing the Adapter Consumer for %s') % topic)
self.proxy = proxy
+ self.pool = greenpool.GreenPool(1024)
super(AdapterConsumer, self).__init__(connection=connection,
topic=topic)
+ def receive(self, *args, **kwargs):
+ self.pool.spawn_n(self._receive, *args, **kwargs)
+
@exception.wrap_exception
- def receive(self, message_data, message):
+ def _receive(self, message_data, message):
"""Magically looks for a method on the proxy object and calls it
Message data should be a dictionary with two keys:
From a6ec10460b60a6c0073ddcc4790b1fb18a675f1b Mon Sep 17 00:00:00 2001
From: Thierry Carrez
Date: 2011年2月15日 15:02:50 +0100
Subject: [PATCH 070/109] Adding missing scripts and files to setup.py /
MANIFEST.in
---
MANIFEST.in | 9 +++++++++
setup.py | 7 ++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 3908830d79ef..510f1d688bdd 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -6,14 +6,23 @@ graft doc
graft smoketests
graft tools
graft etc
+graft bzrplugins
+graft contrib
+graft locale
+graft plugins
include nova/api/openstack/notes.txt
+include nova/auth/*.schema
include nova/auth/novarc.template
+include nova/auth/opendj.sh
include nova/auth/slap.sh
include nova/cloudpipe/bootscript.sh
include nova/cloudpipe/client.ovpn.template
+include nova/cloudpipe/bootscript.template
include nova/compute/fakevirtinstance.xml
include nova/compute/interfaces.template
+include nova/console/xvp.conf.template
include nova/db/sqlalchemy/migrate_repo/migrate.cfg
+include nova/db/sqlalchemy/migrate_repo/README
include nova/virt/interfaces.template
include nova/virt/libvirt*.xml.template
include nova/tests/CA/
diff --git a/setup.py b/setup.py
index e3c45ce3e009..4e25f43ed18f 100644
--- a/setup.py
+++ b/setup.py
@@ -85,9 +85,13 @@ setup(name='nova',
packages=find_packages(exclude=['bin', 'smoketests']),
include_package_data=True,
test_suite='nose.collector',
- scripts=['bin/nova-api',
+ scripts=['bin/nova-ajax-console-proxy',
+ 'bin/nova-api',
+ 'bin/nova-combined',
'bin/nova-compute',
+ 'bin/nova-console',
'bin/nova-dhcpbridge',
+ 'bin/nova-direct-api',
'bin/nova-import-canonical-imagestore',
'bin/nova-instancemonitor',
'bin/nova-logspool',
@@ -96,5 +100,6 @@ setup(name='nova',
'bin/nova-objectstore',
'bin/nova-scheduler',
'bin/nova-spoolsentry',
+ 'bin/stack',
'bin/nova-volume',
'tools/nova-debug'])
From 5dd9839c333e4c11de07ee0bad185252b3b4837c Mon Sep 17 00:00:00 2001
From: Thierry Carrez
Date: 2011年2月15日 15:44:46 +0100
Subject: [PATCH 071/109] Translations will be shipped in po/, not locale/
---
MANIFEST.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 510f1d688bdd..9b6a3b2da610 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -8,7 +8,7 @@ graft tools
graft etc
graft bzrplugins
graft contrib
-graft locale
+graft po
graft plugins
include nova/api/openstack/notes.txt
include nova/auth/*.schema
From aaf5110fa6720d5b32db9b8a41b353a446163396 Mon Sep 17 00:00:00 2001
From: Thierry Carrez
Date: 2011年2月15日 15:53:58 +0100
Subject: [PATCH 072/109] Missing nova/tests/db/nova.austin.sqlite file
---
MANIFEST.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/MANIFEST.in b/MANIFEST.in
index 9b6a3b2da610..f0a9cffb3ac6 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -34,6 +34,7 @@ include nova/tests/bundle/1mb.manifest.xml
include nova/tests/bundle/1mb.no_kernel_or_ramdisk.manifest.xml
include nova/tests/bundle/1mb.part.0
include nova/tests/bundle/1mb.part.1
+include nova/tests/db/nova.austin.sqlite
include plugins/xenapi/README
include plugins/xenapi/etc/xapi.d/plugins/objectstore
include plugins/xenapi/etc/xapi.d/plugins/pluginlib_nova.py
From 5a988eb393c306097250a7f17ea65f0919fd9219 Mon Sep 17 00:00:00 2001
From: Sandy Walsh
Date: 2011年2月15日 12:43:29 -0400
Subject: [PATCH 073/109] fixed / renamed migration scripts
---
.../versions/{003_cactus.py => 003_add_zone_tables.py} | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename nova/db/sqlalchemy/migrate_repo/versions/{003_cactus.py => 003_add_zone_tables.py} (96%)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_add_zone_tables.py
similarity index 96%
rename from nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
rename to nova/db/sqlalchemy/migrate_repo/versions/003_add_zone_tables.py
index eb3287077598..d2b6b95704d8 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/003_add_zone_tables.py
@@ -26,7 +26,7 @@ meta = MetaData()
#
# New Tables
#
-child_zones = Table('child_zones', meta,
+zones = Table('zones', meta,
Column('created_at', DateTime(timezone=False)),
Column('updated_at', DateTime(timezone=False)),
Column('deleted_at', DateTime(timezone=False)),
@@ -55,7 +55,7 @@ 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 (child_zones, ):
+ for table in (zones, ):
try:
table.create()
except Exception:
From cbe4ee9f3a6bf207d475ca230032ced9325c3b7a Mon Sep 17 00:00:00 2001
From: brian-lamar
Date: 2011年2月15日 15:33:17 -0500
Subject: [PATCH 074/109] Added teammate Naveed to authors file for his help.
---
Authors | 1 +
1 file changed, 1 insertion(+)
diff --git a/Authors b/Authors
index e8bb584568c6..03f30084d68f 100644
--- a/Authors
+++ b/Authors
@@ -43,6 +43,7 @@ Monty Taylor
MORITA Kazutaka
Muneyuki Noguchi
Nachi Ueno
+Naveed Massjouni
Paul Voccio
Ricardo Carrillo Cruz
Rick Clark
From 93df4098bf4a2b1f161eb5e6675d3a4fd820d1ed Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月15日 22:13:08 +0100
Subject: [PATCH 075/109] Don't hid RotatingFileHandler behind FileHandler's
name.
---
nova/log.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/log.py b/nova/log.py
index 413dd946b455..82a13f6a8b37 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -92,7 +92,7 @@ critical = logging.critical
log = logging.log
# handlers
StreamHandler = logging.StreamHandler
-FileHandler = logging.RotatingFileHandler
+RotatingFileHandler = logging.RotatingFileHandler
# logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler.
SysLogHandler = logging.handlers.SysLogHandler
@@ -124,7 +124,7 @@ def basicConfig():
syslog.setFormatter(_formatter)
logging.root.addHandler(syslog)
if FLAGS.logfile:
- logfile = FileHandler(FLAGS.logfile)
+ logfile = RotatingFileHandler(FLAGS.logfile)
logfile.setFormatter(_formatter)
logging.root.addHandler(logfile)
From 503749849df73df1732583bc9452e7952bf78ac2 Mon Sep 17 00:00:00 2001
From: Trey Morris Date: 2011年2月15日 15:25:48 -0600
Subject: [PATCH 076/109] moved reset network to after boot durrrrr...
---
nova/virt/xenapi/vmops.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 00028cdaa359..dd9f48ddfc39 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -134,9 +134,6 @@ class VMOps(object):
VMHelper.create_vif(self._session, vm_ref,
network_ref, instance.mac_address)
- # call reset networking
- self.reset_network(instance)
-
LOG.debug(_('Starting VM %s...'), vm_ref)
self._session.call_xenapi('VM.start', vm_ref, False, False)
instance_name = instance.name
@@ -164,6 +161,10 @@ class VMOps(object):
timer.stop()
timer.f = _wait_for_boot
+
+ # call reset networking
+ self.reset_network(instance)
+
return timer.start(interval=0.5, now=True)
def _get_vm_opaque_ref(self, instance_or_vm):
From 9b4150ad66abcdeb5dd46927fa320f9f2c6c99f6 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: 2011年2月15日 15:30:44 -0600
Subject: [PATCH 077/109] changed d to s
---
locale/nova.pot | 2 +-
nova/virt/xenapi/vm_utils.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/locale/nova.pot b/locale/nova.pot
index a96411e33922..53e38c619160 100644
--- a/locale/nova.pot
+++ b/locale/nova.pot
@@ -1826,7 +1826,7 @@ msgstr ""
#: nova/virt/xenapi/vm_utils.py:290
#, python-format
-msgid "PV Kernel in VDI:%d"
+msgid "PV Kernel in VDI:%s"
msgstr ""
#: nova/virt/xenapi/vm_utils.py:318
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 574ef0944681..80cc3035d7fe 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -394,7 +394,7 @@ class VMHelper(HelperBase):
pv = True
elif pv_str.lower() == 'false':
pv = False
- LOG.debug(_("PV Kernel in VDI:%d"), pv)
+ LOG.debug(_("PV Kernel in VDI:%s"), pv)
return pv
@classmethod
From 52a443dfb53e8fa2226e7ae8b8dac0fa6e32a69d Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月15日 23:11:51 +0100
Subject: [PATCH 078/109] Refactor code that decides which logfile to use, if
any.
Adds unit tests.
---
nova/log.py | 17 +++++++++++------
nova/tests/test_log.py | 21 +++++++++++++++++++++
2 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/nova/log.py b/nova/log.py
index a5b4828d5c82..ec6681eddf4a 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -112,6 +112,15 @@ def _dictify_context(context):
context = context.to_dict()
return context
+def _get_binary_name():
+ return os.path.basename(inspect.stack()[-1][1])
+
+def get_log_file_path(binary=None):
+ if FLAGS.logfile:
+ return FLAGS.logfile
+ if FLAGS.logdir:
+ binary = binary or _get_binary_name()
+ return '%s.log' % (os.path.join(FLAGS.logdir, binary),)
def basicConfig():
logging.basicConfig()
@@ -125,12 +134,8 @@ def basicConfig():
syslog = SysLogHandler(address='/dev/log')
syslog.setFormatter(_formatter)
logging.root.addHandler(syslog)
- if FLAGS.logfile or FLAGS.logdir:
- if FLAGS.logfile:
- logfile = FLAGS.logfile
- else:
- binary = os.path.basename(inspect.stack()[-1][1])
- logpath = '%s.log' % (os.path.join(FLAGS.logdir, binary),)
+ logpath = get_log_file_path()
+ if logpath:
logfile = FileHandler(logpath)
logfile.setFormatter(_formatter)
logging.root.addHandler(logfile)
diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py
index 868a5ead392e..7a5c5293542e 100644
--- a/nova/tests/test_log.py
+++ b/nova/tests/test_log.py
@@ -46,6 +46,27 @@ class RootLoggerTestCase(test.TestCase):
self.assert_(True) # didn't raise exception
+class LogHandlerTestCase(test.TestCase):
+ def test_log_path_logdir(self):
+ self.flags(logdir='/some/path')
+ self.assertEquals(log.get_log_file_path(binary='foo-bar'),
+ '/some/path/foo-bar.log')
+
+ def test_log_path_logfile(self):
+ self.flags(logfile='/some/path/foo-bar.log')
+ self.assertEquals(log.get_log_file_path(binary='foo-bar'),
+ '/some/path/foo-bar.log')
+
+ def test_log_path_none(self):
+ self.assertIsNone(log.get_log_file_path(binary='foo-bar'))
+
+ def test_log_path_logfile_overrides_logdir(self):
+ self.flags(logdir='/some/other/path',
+ logfile='/some/path/foo-bar.log')
+ self.assertEquals(log.get_log_file_path(binary='foo-bar'),
+ '/some/path/foo-bar.log')
+
+
class NovaFormatterTestCase(test.TestCase):
def setUp(self):
super(NovaFormatterTestCase, self).setUp()
From 297fdbfead55017340150412cd00b3aa7b962257 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月15日 23:13:25 +0100
Subject: [PATCH 079/109] Don't hide RotatingFileHandler behind FileHandler's
name.
---
nova/log.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/log.py b/nova/log.py
index 413dd946b455..32b6d46291f4 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -92,7 +92,7 @@ critical = logging.critical
log = logging.log
# handlers
StreamHandler = logging.StreamHandler
-FileHandler = logging.RotatingFileHandler
+RotatingFileHandler = logging.handlers.RotatingFileHandler
# logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler.
SysLogHandler = logging.handlers.SysLogHandler
@@ -124,7 +124,7 @@ def basicConfig():
syslog.setFormatter(_formatter)
logging.root.addHandler(syslog)
if FLAGS.logfile:
- logfile = FileHandler(FLAGS.logfile)
+ logfile = RotatingFileHandler(FLAGS.logfile)
logfile.setFormatter(_formatter)
logging.root.addHandler(logfile)
From 00f785dab7d269b9baf403f51bc1d4b2ea1dc06a Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月16日 00:10:17 +0100
Subject: [PATCH 080/109] Make rpc thread pool size configurable.
---
nova/rpc.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/nova/rpc.py b/nova/rpc.py
index efe6164add38..067d954d0208 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -43,6 +43,8 @@ from nova import utils
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.rpc')
+FLAGS.DEFINE_integer('rpc_thread_pool_size', 1024, 'Size of RPC thread pool')
+
class Connection(carrot_connection.BrokerConnection):
"""Connection instance object"""
@@ -156,7 +158,7 @@ class AdapterConsumer(TopicConsumer):
def __init__(self, connection=None, topic="broadcast", proxy=None):
LOG.debug(_('Initing the Adapter Consumer for %s') % topic)
self.proxy = proxy
- self.pool = greenpool.GreenPool(1024)
+ self.pool = greenpool.GreenPool(FLAGS.rpc_thread_pool_size)
super(AdapterConsumer, self).__init__(connection=connection,
topic=topic)
From 1b9413e11ba1b4b49b50965e3f812e636f2319d5 Mon Sep 17 00:00:00 2001
From: Trey Morris Date: 2011年2月15日 18:20:44 -0600
Subject: [PATCH 082/109] stubbed out reset networkin xenapi VM tests to solve
domid problem
---
nova/tests/test_xenapi.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index d5660c5d1f92..6b8efc9d8770 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -32,6 +32,7 @@ from nova.virt import xenapi_conn
from nova.virt.xenapi import fake as xenapi_fake
from nova.virt.xenapi import volume_utils
from nova.virt.xenapi.vmops import SimpleDH
+from nova.virt.xenapi.vmops import VMOps
from nova.tests.db import fakes as db_fakes
from nova.tests.xenapi import stubs
from nova.tests.glance import stubs as glance_stubs
@@ -141,6 +142,10 @@ class XenAPIVolumeTestCase(test.TestCase):
self.stubs.UnsetAll()
+def reset_network(*args):
+ pass
+
+
class XenAPIVMTestCase(test.TestCase):
"""
Unit tests for VM operations
@@ -162,6 +167,7 @@ class XenAPIVMTestCase(test.TestCase):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
stubs.stubout_get_this_vm_uuid(self.stubs)
stubs.stubout_stream_disk(self.stubs)
+ self.stubs.Set(VMOps, 'reset_network', reset_network)
glance_stubs.stubout_glance_client(self.stubs,
glance_stubs.FakeGlance)
self.conn = xenapi_conn.get_connection(False)
From 333ee7dd74b187ec48e923f767267eb9bb29a4aa Mon Sep 17 00:00:00 2001
From: Vasiliy Shlykov
Date: 2011年2月16日 10:13:52 +0300
Subject: [PATCH 083/109] Added myself to the authors file.
---
Authors | 1 +
1 file changed, 1 insertion(+)
diff --git a/Authors b/Authors
index b359fec221c6..c338a9b3ed9b 100644
--- a/Authors
+++ b/Authors
@@ -56,6 +56,7 @@ Thierry Carrez
Todd Willey
Trey Morris Tushar Patil
+Vasiliy Shlykov
Vishvananda Ishaya
Youcef Laribi
Zhixue Wu
From a00b8b78ce16bbfe4368f0e5e383b7b8f49ba9ef Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月16日 10:02:17 +0100
Subject: [PATCH 084/109] assertIsNone is a 2.7-ism.
---
nova/tests/test_log.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py
index 7a5c5293542e..c2c9d777278d 100644
--- a/nova/tests/test_log.py
+++ b/nova/tests/test_log.py
@@ -58,7 +58,7 @@ class LogHandlerTestCase(test.TestCase):
'/some/path/foo-bar.log')
def test_log_path_none(self):
- self.assertIsNone(log.get_log_file_path(binary='foo-bar'))
+ self.assertTrue(log.get_log_file_path(binary='foo-bar') is None)
def test_log_path_logfile_overrides_logdir(self):
self.flags(logdir='/some/other/path',
From 9f1c46f5be0520afac21c3320e206ced98acd9ba Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月16日 10:46:59 +0100
Subject: [PATCH 085/109] Fix PEP-8 stuff
---
nova/log.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/nova/log.py b/nova/log.py
index ec6681eddf4a..90c40cca7812 100644
--- a/nova/log.py
+++ b/nova/log.py
@@ -112,9 +112,11 @@ def _dictify_context(context):
context = context.to_dict()
return context
+
def _get_binary_name():
return os.path.basename(inspect.stack()[-1][1])
+
def get_log_file_path(binary=None):
if FLAGS.logfile:
return FLAGS.logfile
@@ -122,6 +124,7 @@ def get_log_file_path(binary=None):
binary = binary or _get_binary_name()
return '%s.log' % (os.path.join(FLAGS.logdir, binary),)
+
def basicConfig():
logging.basicConfig()
for handler in logging.root.handlers:
From 7f8595b88df2271c278ae59b6d187b5c3b8dde40 Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: 2011年2月16日 12:15:48 +0100
Subject: [PATCH 086/109] added functionality to nova-manage to list created
networks
---
bin/nova-manage | 12 ++++++++++++
nova/db/api.py | 5 +++++
nova/db/sqlalchemy/api.py | 9 +++++++++
3 files changed, 26 insertions(+)
diff --git a/bin/nova-manage b/bin/nova-manage
index 7835ca551ac5..f7cf77adbf7c 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -499,6 +499,18 @@ class NetworkCommands(object):
vlan_start=int(vlan_start),
vpn_start=int(vpn_start))
+ def list(self):
+ """List all created networks"""
+ print "%-18s\t%-15s\t%-15s\t%-15s" % ('CIDR',
+ 'netmask',
+ 'dhcp_start',
+ 'DNS')
+ for network in db.network_get_all(context.get_admin_context()):
+ print "%-18s\t%-15s\t%-15s\t%-15s" % (network.cidr,
+ network.netmask,
+ network.dhcp_start,
+ network.dns)
+
class ServiceCommands(object):
"""Enable and disable running services"""
diff --git a/nova/db/api.py b/nova/db/api.py
index 789cb8ebbac6..d06f3731fa21 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -500,6 +500,11 @@ def network_get(context, network_id):
return IMPL.network_get(context, network_id)
+def network_get_all(context):
+ """Return all defined networks."""
+ return IMPL.network_get_all(context)
+
+
# pylint: disable-msg=C0103
def network_get_associated_fixed_ips(context, network_id):
"""Get all network's ips that have been associated."""
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 02855e7a919c..a11572947f6e 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1056,6 +1056,15 @@ def network_get(context, network_id, session=None):
return result
+@require_admin_context
+def network_get_all(context):
+ session = get_session()
+ result = session.query(models.Network)
+ if not result:
+ raise exception.NotFound('No networks defined')
+ return result
+
+
# NOTE(vish): pylint complains because of the long method name, but
# it fits with the names of the rest of the methods
# pylint: disable-msg=C0103
From 9be342534ee54f86c274f03d1d4a0c310f08e4ae Mon Sep 17 00:00:00 2001
From: Brian Schott
Date: 2011年2月16日 13:24:46 -0500
Subject: [PATCH 087/109] fixed authors, import sys in migration.py
---
Authors | 2 +-
nova/db/sqlalchemy/migration.py | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/Authors b/Authors
index 0edabc5314f4..29fbf76b36f9 100644
--- a/Authors
+++ b/Authors
@@ -4,7 +4,7 @@ Anthony Young
Antony Messerli
Armando Migliaccio
Bilal Akhtar
-Brian Schott
+Brian Schott
Chiradeep Vittal
Chmouel Boudjnah
Chris Behrens
diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py
index 3b6b4707a76c..d2671e1a3a42 100644
--- a/nova/db/sqlalchemy/migration.py
+++ b/nova/db/sqlalchemy/migration.py
@@ -17,6 +17,7 @@
# under the License.
import os
+import sys
from nova import flags
From 56ebab08b29da2ed9a4ec29bb1c0695371acc142 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月16日 20:34:17 +0100
Subject: [PATCH 088/109] Spell flags correctly (i.e. not in upper case)
---
nova/rpc.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/rpc.py b/nova/rpc.py
index 067d954d0208..205bb524a12b 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -43,7 +43,7 @@ from nova import utils
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.rpc')
-FLAGS.DEFINE_integer('rpc_thread_pool_size', 1024, 'Size of RPC thread pool')
+flags.DEFINE_integer('rpc_thread_pool_size', 1024, 'Size of RPC thread pool')
class Connection(carrot_connection.BrokerConnection):
From 6bd620fa3d378b4fe437e498ef17888bf632b9d6 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: 2011年2月16日 12:02:10 -0800
Subject: [PATCH 089/109] sanitize all args to strings before sending them to
ldap
---
nova/auth/ldapdriver.py | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py
index e652f1caaa60..99cbb43f9a52 100644
--- a/nova/auth/ldapdriver.py
+++ b/nova/auth/ldapdriver.py
@@ -73,6 +73,23 @@ LOG = logging.getLogger("nova.ldapdriver")
# creating this now because I'm expecting an auth refactor
# in which we may want to change the interface a bit more.
+def _clean(attr):
+ """Clean attr for insertion into ldap"""
+ if attr is None:
+ return None
+ if type(attr) is unicode:
+ return str(attr)
+ return attr
+
+def sanitize(fn):
+ """Decorator to sanitize all args"""
+ def _wrapped(self, *args, **kwargs):
+ args = [_clean(x) for x in args]
+ kwargs = dict((k, _clean(v)) for (k, v) in kwargs)
+ return fn(self, *args, **kwargs)
+ _wrapped.func_name = fn.func_name
+ return _wrapped
+
class LdapDriver(object):
"""Ldap Auth driver
@@ -106,23 +123,27 @@ class LdapDriver(object):
self.conn.unbind_s()
return False
+ @sanitize
def get_user(self, uid):
"""Retrieve user by id"""
attr = self.__get_ldap_user(uid)
return self.__to_user(attr)
+ @sanitize
def get_user_from_access_key(self, access):
"""Retrieve user by access key"""
query = '(accessKey=%s)' % access
dn = FLAGS.ldap_user_subtree
return self.__to_user(self.__find_object(dn, query))
+ @sanitize
def get_project(self, pid):
"""Retrieve project by id"""
dn = self.__project_to_dn(pid)
attr = self.__find_object(dn, LdapDriver.project_pattern)
return self.__to_project(attr)
+ @sanitize
def get_users(self):
"""Retrieve list of users"""
attrs = self.__find_objects(FLAGS.ldap_user_subtree,
@@ -134,6 +155,7 @@ class LdapDriver(object):
users.append(user)
return users
+ @sanitize
def get_projects(self, uid=None):
"""Retrieve list of projects"""
pattern = LdapDriver.project_pattern
@@ -143,6 +165,7 @@ class LdapDriver(object):
pattern)
return [self.__to_project(attr) for attr in attrs]
+ @sanitize
def create_user(self, name, access_key, secret_key, is_admin):
"""Create a user"""
if self.__user_exists(name):
@@ -196,6 +219,7 @@ class LdapDriver(object):
self.conn.add_s(self.__uid_to_dn(name), attr)
return self.__to_user(dict(attr))
+ @sanitize
def create_project(self, name, manager_uid,
description=None, member_uids=None):
"""Create a project"""
@@ -231,6 +255,7 @@ class LdapDriver(object):
self.conn.add_s(dn, attr)
return self.__to_project(dict(attr))
+ @sanitize
def modify_project(self, project_id, manager_uid=None, description=None):
"""Modify an existing project"""
if not manager_uid and not description:
@@ -249,21 +274,25 @@ class LdapDriver(object):
dn = self.__project_to_dn(project_id)
self.conn.modify_s(dn, attr)
+ @sanitize
def add_to_project(self, uid, project_id):
"""Add user to project"""
dn = self.__project_to_dn(project_id)
return self.__add_to_group(uid, dn)
+ @sanitize
def remove_from_project(self, uid, project_id):
"""Remove user from project"""
dn = self.__project_to_dn(project_id)
return self.__remove_from_group(uid, dn)
+ @sanitize
def is_in_project(self, uid, project_id):
"""Check if user is in project"""
dn = self.__project_to_dn(project_id)
return self.__is_in_group(uid, dn)
+ @sanitize
def has_role(self, uid, role, project_id=None):
"""Check if user has role
@@ -273,6 +302,7 @@ class LdapDriver(object):
role_dn = self.__role_to_dn(role, project_id)
return self.__is_in_group(uid, role_dn)
+ @sanitize
def add_role(self, uid, role, project_id=None):
"""Add role for user (or user and project)"""
role_dn = self.__role_to_dn(role, project_id)
@@ -283,11 +313,13 @@ class LdapDriver(object):
else:
return self.__add_to_group(uid, role_dn)
+ @sanitize
def remove_role(self, uid, role, project_id=None):
"""Remove role for user (or user and project)"""
role_dn = self.__role_to_dn(role, project_id)
return self.__remove_from_group(uid, role_dn)
+ @sanitize
def get_user_roles(self, uid, project_id=None):
"""Retrieve list of roles for user (or user and project)"""
if project_id is None:
@@ -307,6 +339,7 @@ class LdapDriver(object):
roles = self.__find_objects(project_dn, query)
return [role['cn'][0] for role in roles]
+ @sanitize
def delete_user(self, uid):
"""Delete a user"""
if not self.__user_exists(uid):
@@ -332,12 +365,14 @@ class LdapDriver(object):
# Delete entry
self.conn.delete_s(self.__uid_to_dn(uid))
+ @sanitize
def delete_project(self, project_id):
"""Delete a project"""
project_dn = self.__project_to_dn(project_id)
self.__delete_roles(project_dn)
self.__delete_group(project_dn)
+ @sanitize
def modify_user(self, uid, access_key=None, secret_key=None, admin=None):
"""Modify an existing user"""
if not access_key and not secret_key and admin is None:
From 5b82416998369203f07f3b3adef9570622caa369 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: 2011年2月16日 12:03:59 -0800
Subject: [PATCH 090/109] pep8 fixes
---
nova/auth/ldapdriver.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py
index 99cbb43f9a52..5da7751a015a 100644
--- a/nova/auth/ldapdriver.py
+++ b/nova/auth/ldapdriver.py
@@ -73,6 +73,7 @@ LOG = logging.getLogger("nova.ldapdriver")
# creating this now because I'm expecting an auth refactor
# in which we may want to change the interface a bit more.
+
def _clean(attr):
"""Clean attr for insertion into ldap"""
if attr is None:
@@ -81,6 +82,7 @@ def _clean(attr):
return str(attr)
return attr
+
def sanitize(fn):
"""Decorator to sanitize all args"""
def _wrapped(self, *args, **kwargs):
From c6b8f129ae57da2ea0cd844150e58d4fac7eb71d Mon Sep 17 00:00:00 2001
From: Trey Morris Date: 2011年2月16日 14:12:54 -0600
Subject: [PATCH 091/109] added test for reset_network to openstack api tests,
tabstop 5 to 4, renamed migration
---
nova/api/openstack/__init__.py | 1 +
nova/compute/api.py | 2 +-
.../{003_cactus.py => 003_add_label_to_networks.py} | 0
nova/tests/api/openstack/test_servers.py | 12 ++++++++++++
4 files changed, 14 insertions(+), 1 deletion(-)
rename nova/db/sqlalchemy/migrate_repo/versions/{003_cactus.py => 003_add_label_to_networks.py} (100%)
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 056c7dd27fff..dc3738d4adfa 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -79,6 +79,7 @@ class APIRouter(wsgi.Router):
server_members["actions"] = "GET"
server_members['suspend'] = 'POST'
server_members['resume'] = 'POST'
+ server_members['reset_network'] = 'POST'
mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'},
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 857028605da8..71879b5b7d94 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1,4 +1,4 @@
-# vim: tabstop=5 shiftwidth=4 softtabstop=4
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
similarity index 100%
rename from nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py
rename to nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 724f14f19ccd..89e192eeda79 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -281,6 +281,18 @@ class ServersTest(unittest.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 202)
+ def test_server_reset_network(self):
+ FLAGS.allow_admin_api = True
+ body = dict(server=dict(
+ name='server_test', imageId=2, flavorId=2, metadata={},
+ personality={}))
+ req = webob.Request.blank('/v1.0/servers/1/reset_network')
+ req.method = 'POST'
+ req.content_type = 'application/json'
+ req.body = json.dumps(body)
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 202)
+
def test_server_diagnostics(self):
req = webob.Request.blank("/v1.0/servers/1/diagnostics")
req.method = "GET"
From 5f0340504784c1a0847e5b19aa9a317d9be16c20 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: 2011年2月16日 16:19:57 -0500
Subject: [PATCH 092/109] Added alternate email to mailmap
---
.mailmap | 1 +
Authors | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mailmap b/.mailmap
index c6f6c9a8b628..a05520884970 100644
--- a/.mailmap
+++ b/.mailmap
@@ -34,3 +34,4 @@
+
diff --git a/Authors b/Authors
index c57ca8aedb81..eb9d540bfc9f 100644
--- a/Authors
+++ b/Authors
@@ -4,7 +4,6 @@ Anthony Young
Antony Messerli
Armando Migliaccio
Bilal Akhtar
-Brian Lamar
Chiradeep Vittal
Chmouel Boudjnah
Chris Behrens
From 05b96f9ddd0cc54c74c55c170b2037eeeafb527a Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: 2011年2月16日 16:22:16 -0500
Subject: [PATCH 093/109] Accidently removed myself from Authors.
---
Authors | 1 +
1 file changed, 1 insertion(+)
diff --git a/Authors b/Authors
index eb9d540bfc9f..c57ca8aedb81 100644
--- a/Authors
+++ b/Authors
@@ -4,6 +4,7 @@ Anthony Young
Antony Messerli
Armando Migliaccio
Bilal Akhtar
+Brian Lamar
Chiradeep Vittal
Chmouel Boudjnah
Chris Behrens
From 6b5823f0aa75707fad6ca38dde490a47b740c3da Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: 2011年2月16日 16:40:40 -0500
Subject: [PATCH 094/109] Flipped mailmap entries
---
.mailmap | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mailmap b/.mailmap
index a05520884970..b2287d65f3a2 100644
--- a/.mailmap
+++ b/.mailmap
@@ -34,4 +34,4 @@
-
+
From 5faa6e59ff9dff02e8d583e6711bd08dd1f821fd Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: 2011年2月16日 14:15:41 -0800
Subject: [PATCH 095/109] add periodic disassociate from VlanManager to
FlatDHCPManager.
---
nova/network/manager.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 8eb9f041b17c..a4a4c6064102 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -394,6 +394,18 @@ class FlatDHCPManager(FlatManager):
like FlatDHCPManager.
"""
+ def periodic_tasks(self, context=None):
+ """Tasks to be run at a periodic interval."""
+ super(FlatDHCPManager, self).periodic_tasks(context)
+ now = datetime.datetime.utcnow()
+ timeout = FLAGS.fixed_ip_disassociate_timeout
+ time = now - datetime.timedelta(seconds=timeout)
+ num = self.db.fixed_ip_disassociate_all_by_timeout(context,
+ self.host,
+ time)
+ if num:
+ LOG.debug(_("Dissassociated %s stale fixed ip(s)"), num)
+
def init_host(self):
"""Do any initialization that needs to be run if this is a
standalone service.
From f50101fcf845e93637f50e426ceb759641a20b76 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月17日 13:46:24 +0100
Subject: [PATCH 096/109] Make eth0 the default for FLAGS.public_interface.
---
nova/network/linux_net.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index c1cbff7d8aee..535ce87bcb70 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -44,7 +44,7 @@ flags.DEFINE_string('dhcp_domain',
flags.DEFINE_string('networks_path', '$state_path/networks',
'Location to keep network config files')
-flags.DEFINE_string('public_interface', 'vlan1',
+flags.DEFINE_string('public_interface', 'eth0',
'Interface for public IP addresses')
flags.DEFINE_string('vlan_interface', 'eth0',
'network device for vlans')
From bf7ec579786d3f02e63fc870fdfcb1e9c2674f05 Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: 2011年2月17日 15:14:45 +0100
Subject: [PATCH 097/109] added i18n of 'No networks defined'
---
nova/db/sqlalchemy/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index a11572947f6e..65436ab0f2b9 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1061,7 +1061,7 @@ def network_get_all(context):
session = get_session()
result = session.query(models.Network)
if not result:
- raise exception.NotFound('No networks defined')
+ raise exception.NotFound(_('No networks defined'))
return result
From e28ce7f82d1c89ab0c4e5ebfa98c12f502a33138 Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: 2011年2月17日 09:48:16 -0500
Subject: [PATCH 098/109] removing superfluous pass statements; replacing list
comprehension with for loop; alphabetizing imports
---
nova/api/openstack/servers.py | 6 ++----
nova/tests/api/openstack/test_servers.py | 2 +-
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 60f3d96e3c25..312d83ba5f39 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -72,15 +72,13 @@ def _translate_detail_keys(inst):
inst_dict['addresses']['private'].append(private_ip)
except KeyError:
LOG.debug(_("Failed to read private ip"))
- pass
# grab all public floating ips
try:
- [inst_dict['addresses']['public'].append(floating['address']) \
- for floating in inst['fixed_ip']['floating_ips']]
+ for floating in inst['fixed_ip']['floating_ips']:
+ inst_dict['addresses']['public'].append(floating['address'])
except KeyError:
LOG.debug(_("Failed to read public ip(s)"))
- pass
inst_dict['metadata'] = {}
inst_dict['hostId'] = ''
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 816a0ab8c6ed..ace3b6850276 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -15,9 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import datetime
import json
import unittest
-import datetime
import stubout
import webob
From 5b97ee78b1bc2073bca0204caf92ae4560ec1e8e Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: 2011年2月17日 16:46:55 +0100
Subject: [PATCH 099/109] added more I18N
---
bin/nova-manage | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index f7cf77adbf7c..c64277908354 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -501,9 +501,9 @@ class NetworkCommands(object):
def list(self):
"""List all created networks"""
- print "%-18s\t%-15s\t%-15s\t%-15s" % ('CIDR',
- 'netmask',
- 'dhcp_start',
+ print "%-18s\t%-15s\t%-15s\t%-15s" % (_('network'),
+ _('netmask'),
+ _('start address'),
'DNS')
for network in db.network_get_all(context.get_admin_context()):
print "%-18s\t%-15s\t%-15s\t%-15s" % (network.cidr,
From 7bb9e4c598f829a16cc6444346e087ddb506182a Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: 2011年2月17日 16:58:00 +0100
Subject: [PATCH 100/109] added new functionality to list all defined fixed ips
---
bin/nova-manage | 31 +++++++++++++++++++++++++++++++
nova/db/api.py | 5 +++++
nova/db/sqlalchemy/api.py | 11 +++++++++++
3 files changed, 47 insertions(+)
diff --git a/bin/nova-manage b/bin/nova-manage
index e4c0684c4ba4..a8c9441e26ec 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -433,6 +433,37 @@ class ProjectCommands(object):
"nova-api server on this host.")
+class FixedIpCommands(object):
+ """Class for managing fixed ip."""
+
+ def list(self, host=None):
+ """Lists all fixed ips (optionally by host) arguments: [host]"""
+ ctxt = context.get_admin_context()
+ if host == None:
+ fixed_ips = db.fixed_ip_get_all(ctxt)
+ else:
+ fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
+
+ print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (_('network'),
+ _('IP address'),
+ _('MAC address'),
+ _('hostname'),
+ _('host'))
+ for fixed_ip in fixed_ips:
+ hostname = None
+ host = None
+ mac_address = None
+ if fixed_ip['instance']:
+ instance = fixed_ip['instance']
+ hostname = instance['hostname']
+ host = instance['host']
+ mac_address = instance['mac_address']
+ print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (
+ fixed_ip['network']['cidr'],
+ fixed_ip['address'],
+ mac_address, hostname, host)
+
+
class FloatingIpCommands(object):
"""Class for managing floating ip."""
diff --git a/nova/db/api.py b/nova/db/api.py
index 789cb8ebbac6..2b621044a30f 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -288,6 +288,11 @@ def fixed_ip_disassociate_all_by_timeout(context, host, time):
return IMPL.fixed_ip_disassociate_all_by_timeout(context, host, time)
+def fixed_ip_get_all(context):
+ """Get all defined fixed ips."""
+ return IMPL.fixed_ip_get_all(context)
+
+
def fixed_ip_get_by_address(context, address):
"""Get a fixed ip by address or raise if it does not exist."""
return IMPL.fixed_ip_get_by_address(context, address)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 02855e7a919c..55f2f28bdd04 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -583,6 +583,17 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time):
return result.rowcount
+@require_admin_context
+def fixed_ip_get_all(context, session=None):
+ if not session:
+ session = get_session()
+ result = session.query(models.FixedIp).all()
+ if not result:
+ raise exception.NotFound(_('No fixed ips defined'))
+
+ return result
+
+
@require_context
def fixed_ip_get_by_address(context, address, session=None):
if not session:
From 0b4641a90e5f51cddccb9886902a90d64ceb3200 Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: 2011年2月17日 17:10:51 +0100
Subject: [PATCH 101/109] added entry in the category list
---
bin/nova-manage | 1 +
1 file changed, 1 insertion(+)
diff --git a/bin/nova-manage b/bin/nova-manage
index a8c9441e26ec..ddc78f7e5470 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -653,6 +653,7 @@ CATEGORIES = [
('role', RoleCommands),
('shell', ShellCommands),
('vpn', VpnCommands),
+ ('fixed', FixedIpCommands),
('floating', FloatingIpCommands),
('network', NetworkCommands),
('service', ServiceCommands),
From 28b77765fd38038fd7093589170dead48ffc417f Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: 2011年2月17日 12:13:20 -0500
Subject: [PATCH 102/109] Switched mailmap entries
---
.mailmap | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mailmap b/.mailmap
index b2287d65f3a2..a05520884970 100644
--- a/.mailmap
+++ b/.mailmap
@@ -34,4 +34,4 @@
-
+
From ea4d21b546d9447bac50cf97a62c11129da12d21 Mon Sep 17 00:00:00 2001
From: Trey Morris Date: 2011年2月17日 13:10:37 -0600
Subject: [PATCH 103/109] comments + Englilish, changed copyright in migration,
removed network_get_all from db.api (vestigial)
---
nova/api/openstack/servers.py | 2 +-
nova/compute/api.py | 2 +-
nova/compute/manager.py | 2 +-
nova/db/api.py | 7 +------
.../migrate_repo/versions/003_add_label_to_networks.py | 3 +--
nova/virt/xenapi/vmops.py | 6 ++++++
plugins/xenserver/xenapi/etc/xapi.d/plugins/agent | 2 +-
7 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 8b72704ba156..33cc3bbded35 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -251,7 +251,7 @@ class Controller(wsgi.Controller):
def reset_network(self, req, id):
"""
- admin only operation which resets networking on an instance
+ Reset networking on an instance (admin only).
"""
context = req.environ['nova.context']
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 71879b5b7d94..ed6f0e34afc5 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -468,7 +468,7 @@ class API(base.Base):
def reset_network(self, context, instance_id):
"""
- resets networking on the instance
+ Reset networking on the instance.
"""
self._cast_compute_message('reset_network', context, instance_id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 1e2b95294a43..6fab1a41c2c9 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -501,7 +501,7 @@ class ComputeManager(manager.Manager):
@checks_instance_lock
def reset_network(self, context, instance_id):
"""
- resets the networking on the instance
+ Reset networking on the instance.
"""
context = context.elevated()
diff --git a/nova/db/api.py b/nova/db/api.py
index 850a5126fc13..ce3395932592 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -505,11 +505,6 @@ def network_get(context, network_id):
return IMPL.network_get(context, network_id)
-def network_get_all(context):
- """Get all networks"""
- return IMPL.network_get_all(context)
-
-
# pylint: disable-msg=C0103
def network_get_associated_fixed_ips(context, network_id):
"""Get all network's ips that have been associated."""
@@ -527,7 +522,7 @@ def network_get_by_instance(context, instance_id):
def network_get_all_by_instance(context, instance_id):
- """Get all networks by instance id or raise if it does not exist."""
+ """Get all networks by instance id or raise if none exist."""
return IMPL.network_get_all_by_instance(context, instance_id)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
index ddfe114cb9ae..5ba7910f1d2a 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
@@ -1,7 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2011 OpenStack LLC
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index ea99ff626a94..842e08f22953 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -151,6 +151,8 @@ class VMOps(object):
% locals())
# NOTE(armando): Do we really need to do this in virt?
+ # NOTE(tr3buchet): not sure but wherever we do it, we need to call
+ # reset_network afterwards
timer = utils.LoopingCall(f=None)
def _wait_for_boot():
@@ -437,6 +439,10 @@ class VMOps(object):
return 'http://fakeajaxconsole/fake_url'
def reset_network(self, instance):
+ """
+ Creates uuid arg to pass to make_agent_call and calls it.
+
+ """
args = {'id': str(uuid.uuid4())}
resp = self._make_agent_call('resetnetwork', instance, '', args)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
index 07c7e4df9612..f99ea40828aa 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/agent
@@ -94,7 +94,7 @@ def password(self, arg_dict):
@jsonify
def resetnetwork(self, arg_dict):
"""
- writes a resquest to xenstore that tells the agent to reset the networking
+ Writes a resquest to xenstore that tells the agent to reset networking.
"""
arg_dict['value'] = json.dumps({'name': 'resetnetwork', 'value': ''})
From c2f585952a67aa0c922d7ec80b387e8617587541 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: 2011年2月17日 21:27:48 +0100
Subject: [PATCH 104/109] Re-alphabetise Authors, move extra addressses into
.mailmap.
---
.mailmap | 50 ++++++++++++++++++++++++++++----------------------
Authors | 10 +++++-----
2 files changed, 33 insertions(+), 27 deletions(-)
diff --git a/.mailmap b/.mailmap
index a05520884970..a839eba6c7a5 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,37 +1,43 @@
# Format is:
-#
-
-
-
-
-
-
-
-
-
+#
+#
-
+
+
+
+
+
+
+
+
+
+
+
+
+