nova-2013.1.3-r6 added
This commit is contained in:
parent
7dd9e004bc
commit
b9ebebc297
|
@ -0,0 +1 @@
|
|||
DIST nova-2013.1.3.tar.gz 5780115 SHA256 f7c25186920daccb16867c5fd272318beb8cc076e5a55f79b5906618ef2724f4 SHA512 3de29f1cf0789285c7600796588058f056d4196138584bb5ec13a0ea034bbc0569d116a668db67022e302b29995af5960093af1103996269d73dccd62a5dd238 WHIRLPOOL ebb06733a710764004f99da2a69d5479cdd50e35da6d0992233ab9ca0a7a5854a678c5d184d40f97a66fa3abd052b1e6de4629963dd58292f677707997e56239
|
|
@ -0,0 +1,327 @@
|
|||
From f7aaf1fa04331522aee2158e372940df92f45cb0 Mon Sep 17 00:00:00 2001
|
||||
From: Russell Bryant <rbryant@redhat.com>
|
||||
Date: Thu, 27 Jun 2013 21:00:05 +0000
|
||||
Subject: [PATCH] Make flavors is_public option actually work
|
||||
|
||||
When you create a flavor, you can set an is_public flag to be True or
|
||||
False. It is True by default. When False, the intention is that the
|
||||
flavor is only accessible by an admin, unless you use the flavor_access
|
||||
API extension to grant access to specific tenants.
|
||||
|
||||
Unfortunately, the only place in the code where this was being enforced
|
||||
was when listing flavors through the API. It would filter out the
|
||||
non-public ones for a non-admin. Otherwise, the flavor was accessible.
|
||||
You could get the details, and you could boot an instance with it, if
|
||||
you figured out a valid flavor ID.
|
||||
|
||||
This patch adds enforcement down in the db layer. It also fixes one
|
||||
place in the API where the context wasn't passed down to enable the
|
||||
enforcement to happen.
|
||||
|
||||
Fix bug 1194093.
|
||||
|
||||
master -> grizzly
|
||||
(cherry picked from commit b65d506a5f9d9b2b20777a9aceb44a8ffed6a5de)
|
||||
|
||||
Conflicts:
|
||||
nova/api/openstack/compute/contrib/flavor_access.py
|
||||
nova/api/openstack/compute/contrib/flavormanage.py
|
||||
nova/api/openstack/compute/flavors.py
|
||||
nova/compute/api.py
|
||||
nova/db/sqlalchemy/api.py
|
||||
nova/tests/api/openstack/compute/contrib/test_flavor_access.py
|
||||
nova/tests/api/openstack/compute/contrib/test_flavor_disabled.py
|
||||
nova/tests/api/openstack/compute/contrib/test_flavor_manage.py
|
||||
nova/tests/api/openstack/compute/contrib/test_flavor_rxtx.py
|
||||
nova/tests/api/openstack/compute/contrib/test_flavor_swap.py
|
||||
nova/tests/api/openstack/compute/contrib/test_flavorextradata.py
|
||||
nova/tests/api/openstack/compute/test_flavors.py
|
||||
nova/tests/db/test_db_api.py
|
||||
|
||||
grizzly -> folsom
|
||||
(cherry picked from commit 6df1b7a2a1413a98bffc8b8e0b947f3c90e3bbf5)
|
||||
|
||||
Conflicts:
|
||||
nova/db/sqlalchemy/api.py
|
||||
nova/tests/api/openstack/compute/test_flavors.py
|
||||
|
||||
Change-Id: I5b37fa0bb19683fe1642fd81222547d4a317054e
|
||||
---
|
||||
.../api/openstack/compute/contrib/flavor_access.py | 3 ++-
|
||||
nova/api/openstack/compute/contrib/flavormanage.py | 2 +-
|
||||
nova/api/openstack/compute/flavors.py | 4 +++-
|
||||
nova/compute/api.py | 2 +-
|
||||
nova/compute/instance_types.py | 2 +-
|
||||
nova/db/api.py | 4 ++--
|
||||
nova/db/sqlalchemy/api.py | 26 +++++++++++++++-------
|
||||
.../compute/contrib/test_flavor_access.py | 2 +-
|
||||
.../compute/contrib/test_flavor_disabled.py | 2 +-
|
||||
.../compute/contrib/test_flavor_manage.py | 3 ++-
|
||||
.../openstack/compute/contrib/test_flavor_rxtx.py | 2 +-
|
||||
.../openstack/compute/contrib/test_flavor_swap.py | 2 +-
|
||||
.../compute/contrib/test_flavorextradata.py | 2 +-
|
||||
nova/tests/api/openstack/compute/test_flavors.py | 4 ++--
|
||||
14 files changed, 37 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/nova/api/openstack/compute/contrib/flavor_access.py b/nova/api/openstack/compute/contrib/flavor_access.py
|
||||
index 9991408..26cd77f 100644
|
||||
--- a/nova/api/openstack/compute/contrib/flavor_access.py
|
||||
+++ b/nova/api/openstack/compute/contrib/flavor_access.py
|
||||
@@ -99,7 +99,8 @@ class FlavorAccessController(object):
|
||||
authorize(context)
|
||||
|
||||
try:
|
||||
- flavor = instance_types.get_instance_type_by_flavor_id(flavor_id)
|
||||
+ flavor = instance_types.get_instance_type_by_flavor_id(flavor_id,
|
||||
+ ctxt=context)
|
||||
except exception.FlavorNotFound:
|
||||
explanation = _("Flavor not found.")
|
||||
raise webob.exc.HTTPNotFound(explanation=explanation)
|
||||
diff --git a/nova/api/openstack/compute/contrib/flavormanage.py b/nova/api/openstack/compute/contrib/flavormanage.py
|
||||
index e7731cc..79551b1 100644
|
||||
--- a/nova/api/openstack/compute/contrib/flavormanage.py
|
||||
+++ b/nova/api/openstack/compute/contrib/flavormanage.py
|
||||
@@ -43,7 +43,7 @@ class FlavorManageController(wsgi.Controller):
|
||||
|
||||
try:
|
||||
flavor = instance_types.get_instance_type_by_flavor_id(
|
||||
- id, read_deleted="no")
|
||||
+ id, ctxt=context, read_deleted="no")
|
||||
except exception.NotFound, e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
diff --git a/nova/api/openstack/compute/flavors.py b/nova/api/openstack/compute/flavors.py
|
||||
index 8aa57a2..d51b48a 100644
|
||||
--- a/nova/api/openstack/compute/flavors.py
|
||||
+++ b/nova/api/openstack/compute/flavors.py
|
||||
@@ -84,7 +84,9 @@ class Controller(wsgi.Controller):
|
||||
def show(self, req, id):
|
||||
"""Return data about the given flavor id."""
|
||||
try:
|
||||
- flavor = instance_types.get_instance_type_by_flavor_id(id)
|
||||
+ context = req.environ['nova.context']
|
||||
+ flavor = instance_types.get_instance_type_by_flavor_id(id,
|
||||
+ ctxt=context)
|
||||
req.cache_db_flavor(flavor)
|
||||
except exception.NotFound:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
diff --git a/nova/compute/api.py b/nova/compute/api.py
|
||||
index 5319d04..ca78830 100644
|
||||
--- a/nova/compute/api.py
|
||||
+++ b/nova/compute/api.py
|
||||
@@ -1080,7 +1080,7 @@ class API(base.Base):
|
||||
#NOTE(bcwaldon): this doesn't really belong in this class
|
||||
def get_instance_type(self, context, instance_type_id):
|
||||
"""Get an instance type by instance type id."""
|
||||
- return instance_types.get_instance_type(instance_type_id)
|
||||
+ return instance_types.get_instance_type(instance_type_id, ctxt=context)
|
||||
|
||||
def get(self, context, instance_id):
|
||||
"""Get a single instance with the given instance_id."""
|
||||
diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py
|
||||
index 6869672..5be97c1 100644
|
||||
--- a/nova/compute/instance_types.py
|
||||
+++ b/nova/compute/instance_types.py
|
||||
@@ -163,7 +163,7 @@ def get_instance_type_by_flavor_id(flavorid, ctxt=None, read_deleted="yes"):
|
||||
if ctxt is None:
|
||||
ctxt = context.get_admin_context(read_deleted=read_deleted)
|
||||
|
||||
- return db.instance_type_get_by_flavor_id(ctxt, flavorid)
|
||||
+ return db.instance_type_get_by_flavor_id(ctxt, flavorid, read_deleted)
|
||||
|
||||
|
||||
def get_instance_type_access_by_flavor_id(flavorid, ctxt=None):
|
||||
diff --git a/nova/db/api.py b/nova/db/api.py
|
||||
index 9f2ff73..40db686 100644
|
||||
--- a/nova/db/api.py
|
||||
+++ b/nova/db/api.py
|
||||
@@ -1460,9 +1460,9 @@ def instance_type_get_by_name(context, name):
|
||||
return IMPL.instance_type_get_by_name(context, name)
|
||||
|
||||
|
||||
-def instance_type_get_by_flavor_id(context, id):
|
||||
+def instance_type_get_by_flavor_id(context, id, read_deleted=None):
|
||||
"""Get instance type by flavor id."""
|
||||
- return IMPL.instance_type_get_by_flavor_id(context, id)
|
||||
+ return IMPL.instance_type_get_by_flavor_id(context, id, read_deleted)
|
||||
|
||||
|
||||
def instance_type_destroy(context, name):
|
||||
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
|
||||
index 7fcc4f8..ea32168 100644
|
||||
--- a/nova/db/sqlalchemy/api.py
|
||||
+++ b/nova/db/sqlalchemy/api.py
|
||||
@@ -3910,7 +3910,7 @@ def instance_type_create(context, values):
|
||||
pass
|
||||
try:
|
||||
instance_type_get_by_flavor_id(context, values['flavorid'],
|
||||
- session)
|
||||
+ read_deleted='no', session=session)
|
||||
raise exception.InstanceTypeExists(name=values['name'])
|
||||
except exception.FlavorNotFound:
|
||||
pass
|
||||
@@ -3952,9 +3952,16 @@ def _dict_with_extra_specs(inst_type_query):
|
||||
|
||||
|
||||
def _instance_type_get_query(context, session=None, read_deleted=None):
|
||||
- return model_query(context, models.InstanceTypes, session=session,
|
||||
+ query = model_query(context, models.InstanceTypes, session=session,
|
||||
read_deleted=read_deleted).\
|
||||
- options(joinedload('extra_specs'))
|
||||
+ options(joinedload('extra_specs'))
|
||||
+ if not context.is_admin:
|
||||
+ the_filter = [models.InstanceTypes.is_public == True]
|
||||
+ the_filter.extend([
|
||||
+ models.InstanceTypes.projects.any(project_id=context.project_id)
|
||||
+ ])
|
||||
+ query = query.filter(or_(*the_filter))
|
||||
+ return query
|
||||
|
||||
|
||||
@require_context
|
||||
@@ -4029,9 +4036,11 @@ def instance_type_get_by_name(context, name, session=None):
|
||||
|
||||
|
||||
@require_context
|
||||
-def instance_type_get_by_flavor_id(context, flavor_id, session=None):
|
||||
+def instance_type_get_by_flavor_id(context, flavor_id, read_deleted,
|
||||
+ session=None):
|
||||
"""Returns a dict describing specific flavor_id"""
|
||||
- result = _instance_type_get_query(context, session=session).\
|
||||
+ result = _instance_type_get_query(context, read_deleted=read_deleted,
|
||||
+ session=session).\
|
||||
filter_by(flavorid=flavor_id).\
|
||||
first()
|
||||
|
||||
@@ -4083,7 +4092,7 @@ def instance_type_access_add(context, flavor_id, project_id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
instance_type_ref = instance_type_get_by_flavor_id(context, flavor_id,
|
||||
- session=session)
|
||||
+ read_deleted='no', session=session)
|
||||
instance_type_id = instance_type_ref['id']
|
||||
access_ref = _instance_type_access_query(context, session=session).\
|
||||
filter_by(instance_type_id=instance_type_id).\
|
||||
@@ -4111,7 +4120,7 @@ def instance_type_access_remove(context, flavor_id, project_id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
instance_type_ref = instance_type_get_by_flavor_id(context, flavor_id,
|
||||
- session=session)
|
||||
+ read_deleted='no', session=session)
|
||||
instance_type_id = instance_type_ref['id']
|
||||
access_ref = _instance_type_access_query(context, session=session).\
|
||||
filter_by(instance_type_id=instance_type_id).\
|
||||
@@ -4447,7 +4456,8 @@ def instance_type_extra_specs_update_or_create(context, flavor_id,
|
||||
specs):
|
||||
session = get_session()
|
||||
spec_ref = None
|
||||
- instance_type = instance_type_get_by_flavor_id(context, flavor_id)
|
||||
+ instance_type = instance_type_get_by_flavor_id(context, flavor_id,
|
||||
+ read_deleted='no')
|
||||
for key, value in specs.iteritems():
|
||||
try:
|
||||
spec_ref = instance_type_extra_specs_get_item(
|
||||
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavor_access.py b/nova/tests/api/openstack/compute/contrib/test_flavor_access.py
|
||||
index 0bf1f1b..075810b 100644
|
||||
--- a/nova/tests/api/openstack/compute/contrib/test_flavor_access.py
|
||||
+++ b/nova/tests/api/openstack/compute/contrib/test_flavor_access.py
|
||||
@@ -68,7 +68,7 @@ def fake_get_instance_type_access_by_flavor_id(flavorid):
|
||||
return res
|
||||
|
||||
|
||||
-def fake_get_instance_type_by_flavor_id(flavorid):
|
||||
+def fake_get_instance_type_by_flavor_id(flavorid, ctxt=None):
|
||||
return INSTANCE_TYPES[flavorid]
|
||||
|
||||
|
||||
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavor_disabled.py b/nova/tests/api/openstack/compute/contrib/test_flavor_disabled.py
|
||||
index 1225b56..933178a 100644
|
||||
--- a/nova/tests/api/openstack/compute/contrib/test_flavor_disabled.py
|
||||
+++ b/nova/tests/api/openstack/compute/contrib/test_flavor_disabled.py
|
||||
@@ -44,7 +44,7 @@ FAKE_FLAVORS = {
|
||||
}
|
||||
|
||||
|
||||
-def fake_instance_type_get_by_flavor_id(flavorid):
|
||||
+def fake_instance_type_get_by_flavor_id(flavorid, ctxt=None):
|
||||
return FAKE_FLAVORS['flavor %s' % flavorid]
|
||||
|
||||
|
||||
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py b/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py
|
||||
index 70fd5e4..7174ed2 100644
|
||||
--- a/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py
|
||||
+++ b/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py
|
||||
@@ -25,7 +25,8 @@ from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
|
||||
-def fake_get_instance_type_by_flavor_id(flavorid, read_deleted='yes'):
|
||||
+def fake_get_instance_type_by_flavor_id(flavorid, ctxt=None,
|
||||
+ read_deleted='yes'):
|
||||
if flavorid == 'failtest':
|
||||
raise exception.NotFound("Not found sucka!")
|
||||
elif not str(flavorid) == '1234':
|
||||
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavor_rxtx.py b/nova/tests/api/openstack/compute/contrib/test_flavor_rxtx.py
|
||||
index 52163c7..afa2259 100644
|
||||
--- a/nova/tests/api/openstack/compute/contrib/test_flavor_rxtx.py
|
||||
+++ b/nova/tests/api/openstack/compute/contrib/test_flavor_rxtx.py
|
||||
@@ -43,7 +43,7 @@ FAKE_FLAVORS = {
|
||||
}
|
||||
|
||||
|
||||
-def fake_instance_type_get_by_flavor_id(flavorid):
|
||||
+def fake_instance_type_get_by_flavor_id(flavorid, ctxt=None):
|
||||
return FAKE_FLAVORS['flavor %s' % flavorid]
|
||||
|
||||
|
||||
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavor_swap.py b/nova/tests/api/openstack/compute/contrib/test_flavor_swap.py
|
||||
index 75e9cd7..3fd1ae9 100644
|
||||
--- a/nova/tests/api/openstack/compute/contrib/test_flavor_swap.py
|
||||
+++ b/nova/tests/api/openstack/compute/contrib/test_flavor_swap.py
|
||||
@@ -43,7 +43,7 @@ FAKE_FLAVORS = {
|
||||
}
|
||||
|
||||
|
||||
-def fake_instance_type_get_by_flavor_id(flavorid):
|
||||
+def fake_instance_type_get_by_flavor_id(flavorid, ctxt=None):
|
||||
return FAKE_FLAVORS['flavor %s' % flavorid]
|
||||
|
||||
|
||||
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavorextradata.py b/nova/tests/api/openstack/compute/contrib/test_flavorextradata.py
|
||||
index 8f5301a..9654605 100644
|
||||
--- a/nova/tests/api/openstack/compute/contrib/test_flavorextradata.py
|
||||
+++ b/nova/tests/api/openstack/compute/contrib/test_flavorextradata.py
|
||||
@@ -23,7 +23,7 @@ from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
|
||||
-def fake_get_instance_type_by_flavor_id(flavorid):
|
||||
+def fake_get_instance_type_by_flavor_id(flavorid, ctxt=None):
|
||||
return {
|
||||
'id': flavorid,
|
||||
'flavorid': str(flavorid),
|
||||
diff --git a/nova/tests/api/openstack/compute/test_flavors.py b/nova/tests/api/openstack/compute/test_flavors.py
|
||||
index 77d40df..cfa3429 100644
|
||||
--- a/nova/tests/api/openstack/compute/test_flavors.py
|
||||
+++ b/nova/tests/api/openstack/compute/test_flavors.py
|
||||
@@ -54,7 +54,7 @@ FAKE_FLAVORS = {
|
||||
}
|
||||
|
||||
|
||||
-def fake_instance_type_get_by_flavor_id(flavorid):
|
||||
+def fake_instance_type_get_by_flavor_id(flavorid, ctxt=None):
|
||||
return FAKE_FLAVORS['flavor %s' % flavorid]
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ def empty_instance_type_get_all(inactive=False, filters=None):
|
||||
return {}
|
||||
|
||||
|
||||
-def return_instance_type_not_found(flavor_id):
|
||||
+def return_instance_type_not_found(flavor_id, ctxt=None):
|
||||
raise exception.InstanceTypeNotFound(flavor_id=flavor_id)
|
||||
|
||||
|
||||
--
|
||||
1.8.1.5
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
From d4ee081c5c0a5132781235177c430ebcf72b0b0b Mon Sep 17 00:00:00 2001
|
||||
From: Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
Date: Fri, 19 Jul 2013 10:23:59 -0700
|
||||
Subject: [PATCH] Use cached nwinfo for secgroup rules
|
||||
|
||||
This stops a potential DOS with source security groups by using the
|
||||
db cached version of the network info instead of calling out to
|
||||
the network api multiple times.
|
||||
|
||||
Fixes bug 1184041
|
||||
|
||||
Change-Id: Id5f24ecf0e8cce60c27a9aecbc6e606c4c44d6b6
|
||||
(cherry picked from commit 85aac04704350566d6b06aa7a3b99649946c672c)
|
||||
---
|
||||
nova/db/sqlalchemy/api.py | 2 ++
|
||||
nova/tests/test_libvirt.py | 4 +++-
|
||||
nova/tests/test_xenapi.py | 5 +++--
|
||||
nova/virt/firewall.py | 12 +++---------
|
||||
4 files changed, 11 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
|
||||
index 7fcc4f8..6d3b139 100644
|
||||
--- a/nova/db/sqlalchemy/api.py
|
||||
+++ b/nova/db/sqlalchemy/api.py
|
||||
@@ -3649,6 +3649,8 @@ def security_group_rule_get_by_security_group(context, security_group_id,
|
||||
return _security_group_rule_get_query(context, session=session).\
|
||||
filter_by(parent_group_id=security_group_id).\
|
||||
options(joinedload_all('grantee_group.instances.instance_type')).\
|
||||
+ options(joinedload('grantee_group.instances.'
|
||||
+ 'info_cache')).\
|
||||
all()
|
||||
|
||||
|
||||
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
|
||||
index b26a006..e956eb0 100644
|
||||
--- a/nova/tests/test_libvirt.py
|
||||
+++ b/nova/tests/test_libvirt.py
|
||||
@@ -3240,7 +3240,9 @@ class IptablesFirewallTestCase(test.TestCase):
|
||||
from nova.network import linux_net
|
||||
linux_net.iptables_manager.execute = fake_iptables_execute
|
||||
|
||||
- _fake_stub_out_get_nw_info(self.stubs, lambda *a, **kw: network_model)
|
||||
+ from nova.compute import utils as compute_utils
|
||||
+ self.stubs.Set(compute_utils, 'get_nw_info_for_instance',
|
||||
+ lambda instance: network_model)
|
||||
|
||||
network_info = network_model.legacy()
|
||||
self.fw.prepare_instance_filter(instance_ref, network_info)
|
||||
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
|
||||
index 0cf69d6..7a8f9b4 100644
|
||||
--- a/nova/tests/test_xenapi.py
|
||||
+++ b/nova/tests/test_xenapi.py
|
||||
@@ -1690,8 +1690,9 @@ class XenAPIDom0IptablesFirewallTestCase(stubs.XenAPITestBase):
|
||||
network_model = fake_network.fake_get_instance_nw_info(self.stubs,
|
||||
1, spectacular=True)
|
||||
|
||||
- fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs,
|
||||
- lambda *a, **kw: network_model)
|
||||
+ from nova.compute import utils as compute_utils
|
||||
+ self.stubs.Set(compute_utils, 'get_nw_info_for_instance',
|
||||
+ lambda instance: network_model)
|
||||
|
||||
network_info = network_model.legacy()
|
||||
self.fw.prepare_instance_filter(instance_ref, network_info)
|
||||
diff --git a/nova/virt/firewall.py b/nova/virt/firewall.py
|
||||
index a093a35..7c22c86 100644
|
||||
--- a/nova/virt/firewall.py
|
||||
+++ b/nova/virt/firewall.py
|
||||
@@ -17,10 +17,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
+from nova.compute import utils as compute_utils
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
-from nova import network
|
||||
from nova.network import linux_net
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import importutils
|
||||
@@ -405,15 +405,9 @@ class IptablesFirewallDriver(FirewallDriver):
|
||||
fw_rules += [' '.join(args)]
|
||||
else:
|
||||
if rule['grantee_group']:
|
||||
- # FIXME(jkoelker) This needs to be ported up into
|
||||
- # the compute manager which already
|
||||
- # has access to a nw_api handle,
|
||||
- # and should be the only one making
|
||||
- # making rpc calls.
|
||||
- nw_api = network.API()
|
||||
for instance in rule['grantee_group']['instances']:
|
||||
- nw_info = nw_api.get_instance_nw_info(ctxt,
|
||||
- instance)
|
||||
+ nw_info = compute_utils.get_nw_info_for_instance(
|
||||
+ instance)
|
||||
|
||||
ips = [ip['address']
|
||||
for ip in nw_info.fixed_ips()
|
||||
--
|
||||
1.8.1.5
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
From ef5730a4620b409a3b46e46966e3bc6f3a306464 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Nemec <bnemec@us.ibm.com>
|
||||
Date: Thu, 9 May 2013 19:06:45 +0000
|
||||
Subject: [PATCH] Fix problem with long messages in Qpid (from oslo)
|
||||
|
||||
This is commit 478ac3a3e in oslo-incubator
|
||||
|
||||
Qpid has a limitation where it cannot serialize a dict containing a
|
||||
string greater than 65535 characters. This change alters the Qpid
|
||||
implementation to JSON encode the dict before sending it, but only if
|
||||
Qpid would fail to serialize it. This maintains as much backward
|
||||
compatibility as possible, though long messages will still fail if they
|
||||
are sent to an older receiver.
|
||||
|
||||
Even though this change will modify the message format, it will only do
|
||||
it when messages are longer than 65K which would be broken anyway and
|
||||
could cause serious bugs like the one linked below.
|
||||
|
||||
Fixes bug 1215091
|
||||
|
||||
Change-Id: I2f0e88435748bab631d969573d3a598d9e1f7fef
|
||||
---
|
||||
nova/openstack/common/rpc/impl_qpid.py | 47 ++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 47 insertions(+)
|
||||
|
||||
diff --git a/nova/openstack/common/rpc/impl_qpid.py b/nova/openstack/common/rpc/impl_qpid.py
|
||||
index e579316..67388fd 100644
|
||||
--- a/nova/openstack/common/rpc/impl_qpid.py
|
||||
+++ b/nova/openstack/common/rpc/impl_qpid.py
|
||||
@@ -23,6 +23,7 @@ import uuid
|
||||
|
||||
import eventlet
|
||||
import greenlet
|
||||
+import qpid.codec010 as qpid_codec
|
||||
import qpid.messaging
|
||||
import qpid.messaging.exceptions
|
||||
|
||||
@@ -63,6 +64,8 @@ qpid_opts = [
|
||||
|
||||
cfg.CONF.register_opts(qpid_opts)
|
||||
|
||||
+JSON_CONTENT_TYPE = 'application/json; charset=utf8'
|
||||
+
|
||||
|
||||
class ConsumerBase(object):
|
||||
"""Consumer base class."""
|
||||
@@ -117,10 +120,27 @@ class ConsumerBase(object):
|
||||
self.receiver = session.receiver(self.address)
|
||||
self.receiver.capacity = 1
|
||||
|
||||
+ def _unpack_json_msg(self, msg):
|
||||
+ """Load the JSON data in msg if msg.content_type indicates that it
|
||||
+ is necessary. Put the loaded data back into msg.content and
|
||||
+ update msg.content_type appropriately.
|
||||
+
|
||||
+ A Qpid Message containing a dict will have a content_type of
|
||||
+ 'amqp/map', whereas one containing a string that needs to be converted
|
||||
+ back from JSON will have a content_type of JSON_CONTENT_TYPE.
|
||||
+
|
||||
+ :param msg: a Qpid Message object
|
||||
+ :returns: None
|
||||
+ """
|
||||
+ if msg.content_type == JSON_CONTENT_TYPE:
|
||||
+ msg.content = jsonutils.loads(msg.content)
|
||||
+ msg.content_type = 'amqp/map'
|
||||
+
|
||||
def consume(self):
|
||||
"""Fetch the message and pass it to the callback object"""
|
||||
message = self.receiver.fetch()
|
||||
try:
|
||||
+ self._unpack_json_msg(message)
|
||||
self.callback(message.content)
|
||||
except Exception:
|
||||
LOG.exception(_("Failed to process message... skipping it."))
|
||||
@@ -220,8 +240,35 @@ class Publisher(object):
|
||||
"""Re-establish the Sender after a reconnection"""
|
||||
self.sender = session.sender(self.address)
|
||||
|
||||
+ def _pack_json_msg(self, msg):
|
||||
+ """Qpid cannot serialize dicts containing strings longer than 65535
|
||||
+ characters. This function dumps the message content to a JSON
|
||||
+ string, which Qpid is able to handle.
|
||||
+
|
||||
+ :param msg: May be either a Qpid Message object or a bare dict.
|
||||
+ :returns: A Qpid Message with its content field JSON encoded.
|
||||
+ """
|
||||
+ try:
|
||||
+ msg.content = jsonutils.dumps(msg.content)
|
||||
+ except AttributeError:
|
||||
+ # Need to have a Qpid message so we can set the content_type.
|
||||
+ msg = qpid.messaging.Message(jsonutils.dumps(msg))
|
||||
+ msg.content_type = JSON_CONTENT_TYPE
|
||||
+ return msg
|
||||
+
|
||||
def send(self, msg):
|
||||
"""Send a message"""
|
||||
+ try:
|
||||
+ # Check if Qpid can encode the message
|
||||
+ check_msg = msg
|
||||
+ if not hasattr(check_msg, 'content_type'):
|
||||
+ check_msg = qpid.messaging.Message(msg)
|
||||
+ content_type = check_msg.content_type
|
||||
+ enc, dec = qpid.messaging.message.get_codec(content_type)
|
||||
+ enc(check_msg.content)
|
||||
+ except qpid_codec.CodecException:
|
||||
+ # This means the message couldn't be serialized as a dict.
|
||||
+ msg = self._pack_json_msg(msg)
|
||||
self.sender.send(msg)
|
||||
|
||||
|
||||
--
|
||||
1.8.1.2
|
||||
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
From 6825959560e06725d26625fd21f5c0b78b305492 Mon Sep 17 00:00:00 2001
|
||||
From: Russell Bryant <rbryant@redhat.com>
|
||||
Date: Tue, 20 Aug 2013 11:06:12 -0400
|
||||
Subject: [PATCH] Enforce flavor access during instance boot
|
||||
|
||||
The code in the servers API did not pass the context when retrieving
|
||||
flavor details. That means it would use an admin context instead,
|
||||
bypassing all flavor access control checks.
|
||||
|
||||
This patch includes the fix, and the corresponding unit test for the v2
|
||||
API.
|
||||
|
||||
Closes-bug: #1212179
|
||||
|
||||
(cherry picked from commit 4054cc4a22a1fea997dec76afb5646fd6c6ea6b9)
|
||||
|
||||
Conflicts:
|
||||
nova/api/openstack/compute/plugins/v3/servers.py
|
||||
nova/api/openstack/compute/servers.py
|
||||
nova/tests/api/openstack/compute/plugins/v3/test_servers.py
|
||||
nova/tests/api/openstack/compute/test_servers.py
|
||||
|
||||
Change-Id: I681ae9965e19767df22fa74c3315e4e03a459d3b
|
||||
---
|
||||
nova/api/openstack/compute/servers.py | 3 ++-
|
||||
nova/tests/api/openstack/compute/test_servers.py | 23 +++++++++++++++++++++--
|
||||
2 files changed, 23 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
|
||||
index 6908262..ab06595 100644
|
||||
--- a/nova/api/openstack/compute/servers.py
|
||||
+++ b/nova/api/openstack/compute/servers.py
|
||||
@@ -844,7 +844,8 @@ class Controller(wsgi.Controller):
|
||||
|
||||
try:
|
||||
_get_inst_type = instance_types.get_instance_type_by_flavor_id
|
||||
- inst_type = _get_inst_type(flavor_id, read_deleted="no")
|
||||
+ inst_type = _get_inst_type(flavor_id, ctxt=context,
|
||||
+ read_deleted="no")
|
||||
|
||||
(instances, resv_id) = self.compute_api.create(context,
|
||||
inst_type,
|
||||
diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py
|
||||
index cd88a2a..5cb26bd 100644
|
||||
--- a/nova/tests/api/openstack/compute/test_servers.py
|
||||
+++ b/nova/tests/api/openstack/compute/test_servers.py
|
||||
@@ -34,6 +34,7 @@ import nova.compute.api
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import task_states
|
||||
from nova.compute import vm_states
|
||||
+import nova.context
|
||||
import nova.db
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova import flags
|
||||
@@ -1703,10 +1704,10 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
"""
|
||||
self.assertTrue("adminPass" not in server_dict)
|
||||
|
||||
- def _test_create_instance(self):
|
||||
+ def _test_create_instance(self, flavor=2):
|
||||
image_uuid = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
|
||||
body = dict(server=dict(
|
||||
- name='server_test', imageRef=image_uuid, flavorRef=2,
|
||||
+ name='server_test', imageRef=image_uuid, flavorRef=flavor,
|
||||
metadata={'hello': 'world', 'open': 'stack'},
|
||||
personality={}))
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/servers')
|
||||
@@ -1718,6 +1719,24 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
self._check_admin_pass_len(server)
|
||||
self.assertEqual(FAKE_UUID, server['id'])
|
||||
|
||||
+ def test_create_instance_private_flavor(self):
|
||||
+ values = {
|
||||
+ 'name': 'fake_name',
|
||||
+ 'memory_mb': 512,
|
||||
+ 'vcpus': 1,
|
||||
+ 'root_gb': 10,
|
||||
+ 'ephemeral_gb': 10,
|
||||
+ 'flavorid': '1324',
|
||||
+ 'swap': 0,
|
||||
+ 'rxtx_factor': 0.5,
|
||||
+ 'vcpu_weight': 1,
|
||||
+ 'disabled': False,
|
||||
+ 'is_public': False,
|
||||
+ }
|
||||
+ nova.db.instance_type_create(nova.context.get_admin_context(), values)
|
||||
+ self.assertRaises(webob.exc.HTTPBadRequest, self._test_create_instance,
|
||||
+ flavor=1324)
|
||||
+
|
||||
def test_create_server_bad_image_href(self):
|
||||
image_href = 1
|
||||
flavor_ref = 'http://localhost/123/flavors/3'
|
||||
--
|
||||
1.8.1.5
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
From 2d949c415b97ed9649e78c880ab149d0d39c1152 Mon Sep 17 00:00:00 2001
|
||||
From: Xavier Queralt <xqueralt@redhat.com>
|
||||
Date: Thu, 5 Sep 2013 10:08:29 +0200
|
||||
Subject: [PATCH] Fix Qpid when sending long messages (from oslo)
|
||||
|
||||
This is commit 4f97479ad in oslo-incubator
|
||||
|
||||
Qpid has a limitation where it cannot serialize a dict containing a
|
||||
string greater than 65535 characters. This change alters the Qpid
|
||||
implementation to JSON encode the dict before sending it, but only if
|
||||
Qpid would fail to serialize it. This maintains as much backward
|
||||
compatibility as possible, though long messages will still fail if they
|
||||
are sent to an older receiver.
|
||||
|
||||
The first part of this fix was ported to Grizzly in Ib52e9458a to allow
|
||||
receiving messages from Havana using the new format. Even though this
|
||||
change will modify the message format, it will only do it when messages
|
||||
are longer than 65K which would be broken anyway and could cause serious
|
||||
bugs like the one linked below.
|
||||
|
||||
Fixes bug 1215091
|
||||
|
||||
Change-Id: I505b648c3d0e1176ec7a3fc7d1646fa5a5232261
|
||||
---
|
||||
nova/openstack/common/rpc/impl_qpid.py | 28 ++++++++++++++++++++++++++++
|
||||
1 file changed, 28 insertions(+)
|
||||
|
||||
diff --git a/nova/openstack/common/rpc/impl_qpid.py b/nova/openstack/common/rpc/impl_qpid.py
|
||||
index 0044088..a7aebc1 100644
|
||||
--- a/nova/openstack/common/rpc/impl_qpid.py
|
||||
+++ b/nova/openstack/common/rpc/impl_qpid.py
|
||||
@@ -31,6 +31,7 @@ from nova.openstack.common import log as logging
|
||||
from nova.openstack.common.rpc import amqp as rpc_amqp
|
||||
from nova.openstack.common.rpc import common as rpc_common
|
||||
|
||||
+qpid_codec = importutils.try_import("qpid.codec010")
|
||||
qpid_messaging = importutils.try_import("qpid.messaging")
|
||||
qpid_exceptions = importutils.try_import("qpid.messaging.exceptions")
|
||||
|
||||
@@ -247,8 +248,35 @@ class Publisher(object):
|
||||
"""Re-establish the Sender after a reconnection"""
|
||||
self.sender = session.sender(self.address)
|
||||
|
||||
+ def _pack_json_msg(self, msg):
|
||||
+ """Qpid cannot serialize dicts containing strings longer than 65535
|
||||
+ characters. This function dumps the message content to a JSON
|
||||
+ string, which Qpid is able to handle.
|
||||
+
|
||||
+ :param msg: May be either a Qpid Message object or a bare dict.
|
||||
+ :returns: A Qpid Message with its content field JSON encoded.
|
||||
+ """
|
||||
+ try:
|
||||
+ msg.content = jsonutils.dumps(msg.content)
|
||||
+ except AttributeError:
|
||||
+ # Need to have a Qpid message so we can set the content_type.
|
||||
+ msg = qpid_messaging.Message(jsonutils.dumps(msg))
|
||||
+ msg.content_type = JSON_CONTENT_TYPE
|
||||
+ return msg
|
||||
+
|
||||
def send(self, msg):
|
||||
"""Send a message"""
|
||||
+ try:
|
||||
+ # Check if Qpid can encode the message
|
||||
+ check_msg = msg
|
||||
+ if not hasattr(check_msg, 'content_type'):
|
||||
+ check_msg = qpid_messaging.Message(msg)
|
||||
+ content_type = check_msg.content_type
|
||||
+ enc, dec = qpid_messaging.message.get_codec(content_type)
|
||||
+ enc(check_msg.content)
|
||||
+ except qpid_codec.CodecException:
|
||||
+ # This means the message couldn't be serialized as a dict.
|
||||
+ msg = self._pack_json_msg(msg)
|
||||
self.sender.send(msg)
|
||||
|
||||
|
||||
--
|
||||
1.8.1.2
|
||||
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
From 8b686195afe7e6dfb46c56c1ef2fe9c993d8e495 Mon Sep 17 00:00:00 2001
|
||||
From: Russell Bryant <rbryant@redhat.com>
|
||||
Date: Tue, 20 Aug 2013 11:06:12 -0400
|
||||
Subject: [PATCH] Enforce flavor access during instance boot
|
||||
|
||||
The code in the servers API did not pass the context when retrieving
|
||||
flavor details. That means it would use an admin context instead,
|
||||
bypassing all flavor access control checks.
|
||||
|
||||
This patch includes the fix, and the corresponding unit test for the v2
|
||||
API.
|
||||
|
||||
Closes-bug: #1212179
|
||||
|
||||
(cherry picked from commit 4054cc4a22a1fea997dec76afb5646fd6c6ea6b9)
|
||||
|
||||
Conflicts:
|
||||
nova/api/openstack/compute/plugins/v3/servers.py
|
||||
nova/api/openstack/compute/servers.py
|
||||
nova/tests/api/openstack/compute/plugins/v3/test_servers.py
|
||||
nova/tests/api/openstack/compute/test_servers.py
|
||||
|
||||
Change-Id: I681ae9965e19767df22fa74c3315e4e03a459d3b
|
||||
---
|
||||
nova/api/openstack/compute/servers.py | 3 ++-
|
||||
nova/tests/api/openstack/compute/test_servers.py | 22 ++++++++++++++++++++--
|
||||
2 files changed, 22 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
|
||||
index 85ef080..6c38219 100644
|
||||
--- a/nova/api/openstack/compute/servers.py
|
||||
+++ b/nova/api/openstack/compute/servers.py
|
||||
@@ -873,7 +873,8 @@ class Controller(wsgi.Controller):
|
||||
|
||||
try:
|
||||
_get_inst_type = instance_types.get_instance_type_by_flavor_id
|
||||
- inst_type = _get_inst_type(flavor_id, read_deleted="no")
|
||||
+ inst_type = _get_inst_type(flavor_id, ctxt=context,
|
||||
+ read_deleted="no")
|
||||
|
||||
(instances, resv_id) = self.compute_api.create(context,
|
||||
inst_type,
|
||||
diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py
|
||||
index 7748c2e..89d0f8a 100644
|
||||
--- a/nova/tests/api/openstack/compute/test_servers.py
|
||||
+++ b/nova/tests/api/openstack/compute/test_servers.py
|
||||
@@ -1822,10 +1822,10 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
"""utility function - check server_dict for absence of adminPass."""
|
||||
self.assertTrue("adminPass" not in server_dict)
|
||||
|
||||
- def _test_create_instance(self):
|
||||
+ def _test_create_instance(self, flavor=2):
|
||||
image_uuid = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
|
||||
body = dict(server=dict(
|
||||
- name='server_test', imageRef=image_uuid, flavorRef=2,
|
||||
+ name='server_test', imageRef=image_uuid, flavorRef=flavor,
|
||||
metadata={'hello': 'world', 'open': 'stack'},
|
||||
personality={}))
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/servers')
|
||||
@@ -1837,6 +1837,24 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
self._check_admin_pass_len(server)
|
||||
self.assertEqual(FAKE_UUID, server['id'])
|
||||
|
||||
+ def test_create_instance_private_flavor(self):
|
||||
+ values = {
|
||||
+ 'name': 'fake_name',
|
||||
+ 'memory_mb': 512,
|
||||
+ 'vcpus': 1,
|
||||
+ 'root_gb': 10,
|
||||
+ 'ephemeral_gb': 10,
|
||||
+ 'flavorid': '1324',
|
||||
+ 'swap': 0,
|
||||
+ 'rxtx_factor': 0.5,
|
||||
+ 'vcpu_weight': 1,
|
||||
+ 'disabled': False,
|
||||
+ 'is_public': False,
|
||||
+ }
|
||||
+ db.instance_type_create(context.get_admin_context(), values)
|
||||
+ self.assertRaises(webob.exc.HTTPBadRequest, self._test_create_instance,
|
||||
+ flavor=1324)
|
||||
+
|
||||
def test_create_server_bad_image_href(self):
|
||||
image_href = 1
|
||||
flavor_ref = 'http://localhost/123/flavors/3'
|
||||
--
|
||||
1.8.1.5
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#Don't touch this unless you know what you are doing
|
||||
PID_PATH=/run/nova
|
||||
LOCKDIR_PATH=/var/lock/nova
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
From 74aa04e2ca7942cb1e1a86dcbaffeb72d260ccd7 Mon Sep 17 00:00:00 2001
|
||||
From: Russell Bryant <rbryant@redhat.com>
|
||||
Date: Wed, 1 May 2013 09:41:57 -0400
|
||||
Subject: [PATCH] Remove insecure default for signing_dir option.
|
||||
|
||||
The sample api-paste.ini file included an insecure value for the
|
||||
signing_dir option for the keystone authtoken middleware. Comment out
|
||||
the option so that we just rely on the default behavior by default.
|
||||
|
||||
Fix bug 1174608.
|
||||
|
||||
Conflicts:
|
||||
etc/nova/api-paste.ini
|
||||
|
||||
Change-Id: I6189788953d789c34456bbe150b8ed6ce6f68403
|
||||
(cherry picked from commit 58d6879b1caaa750c39c8e452a0634c24ffef2ce)
|
||||
---
|
||||
etc/nova/api-paste.ini | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/etc/nova/api-paste.ini b/etc/nova/api-paste.ini
|
||||
index 3970974..95307b2 100644
|
||||
--- a/etc/nova/api-paste.ini
|
||||
+++ b/etc/nova/api-paste.ini
|
||||
@@ -124,4 +124,7 @@ auth_protocol = http
|
||||
admin_tenant_name = %SERVICE_TENANT_NAME%
|
||||
admin_user = %SERVICE_USER%
|
||||
admin_password = %SERVICE_PASSWORD%
|
||||
-signing_dir = /tmp/keystone-signing-nova
|
||||
+# signing_dir is configurable, but the default behavior of the authtoken
|
||||
+# middleware should be sufficient. It will create a temporary directory
|
||||
+# in the home directory for the user the nova process is running as.
|
||||
+#signing_dir = /var/lib/nova/keystone-signing
|
||||
--
|
||||
1.8.1.5
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
From 6740c4141ea1152529b82cbf6e5b808eaba912e7 Mon Sep 17 00:00:00 2001
|
||||
From: Chet Burgess <cfb@metacloud.com>
|
||||
Date: Thu, 9 May 2013 09:57:28 +0000
|
||||
Subject: [PATCH] Check QCOW2 image size during root disk creation
|
||||
|
||||
glance can only tell us the size of the file, not the virtual
|
||||
size of the QCOW2. As such we need to check the virtual size of
|
||||
the image once its cached and ensure it's <= to the flavor's
|
||||
root disk size.
|
||||
|
||||
Change-Id: I833467284126557eb598b8350a84e10c06292fa9
|
||||
Fixes: bug 1177830
|
||||
(cherry picked from commit 44a8aba1d5da87d54db48079103fdef946666d80)
|
||||
---
|
||||
nova/tests/test_imagebackend.py | 18 ++++++++++++++++++
|
||||
nova/virt/libvirt/imagebackend.py | 12 ++++++++++++
|
||||
2 files changed, 30 insertions(+)
|
||||
|
||||
diff --git a/nova/tests/test_imagebackend.py b/nova/tests/test_imagebackend.py
|
||||
index f0bb718..da14f20 100644
|
||||
--- a/nova/tests/test_imagebackend.py
|
||||
+++ b/nova/tests/test_imagebackend.py
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
import os
|
||||
|
||||
+from nova import exception
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova.tests import fake_libvirt_utils
|
||||
@@ -190,7 +191,10 @@ class Qcow2TestCase(_ImageTestCase):
|
||||
fn = self.prepare_mocks()
|
||||
fn(target=self.TEMPLATE_PATH)
|
||||
self.mox.StubOutWithMock(os.path, 'exists')
|
||||
+ self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||
os.path.exists(self.QCOW2_BASE).AndReturn(False)
|
||||
+ imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
+ ).AndReturn(self.SIZE)
|
||||
imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH,
|
||||
self.QCOW2_BASE)
|
||||
imagebackend.disk.extend(self.QCOW2_BASE, self.SIZE)
|
||||
@@ -203,11 +207,25 @@ class Qcow2TestCase(_ImageTestCase):
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
+ def test_create_image_too_small(self):
|
||||
+ self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||
+ imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
+ ).AndReturn(self.SIZE)
|
||||
+ self.mox.ReplayAll()
|
||||
+
|
||||
+ image = self.image_class(self.INSTANCE, self.NAME)
|
||||
+ self.assertRaises(exception.ImageTooLarge, image.create_image, None,
|
||||
+ self.TEMPLATE_PATH, 1)
|
||||
+ self.mox.VerifyAll()
|
||||
+
|
||||
def test_create_image_with_size_template_exists(self):
|
||||
fn = self.prepare_mocks()
|
||||
fn(target=self.TEMPLATE_PATH)
|
||||
self.mox.StubOutWithMock(os.path, 'exists')
|
||||
+ self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||
os.path.exists(self.QCOW2_BASE).AndReturn(True)
|
||||
+ imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
+ ).AndReturn(self.SIZE)
|
||||
imagebackend.libvirt_utils.create_cow_image(self.QCOW2_BASE,
|
||||
self.PATH)
|
||||
self.mox.ReplayAll()
|
||||
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
|
||||
index 0f2f044..5e7023e 100644
|
||||
--- a/nova/virt/libvirt/imagebackend.py
|
||||
+++ b/nova/virt/libvirt/imagebackend.py
|
||||
@@ -19,14 +19,17 @@ import abc
|
||||
import contextlib
|
||||
import os
|
||||
|
||||
+from nova import exception
|
||||
from nova import flags
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import excutils
|
||||
+from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
from nova.virt.disk import api as disk
|
||||
from nova.virt.libvirt import config
|
||||
from nova.virt.libvirt import utils as libvirt_utils
|
||||
|
||||
+
|
||||
__imagebackend_opts = [
|
||||
cfg.StrOpt('libvirt_images_type',
|
||||
default='default',
|
||||
@@ -46,6 +49,8 @@ __imagebackend_opts = [
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(__imagebackend_opts)
|
||||
|
||||
+LOG = logging.getLogger(__name__)
|
||||
+
|
||||
|
||||
class Image(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
@@ -170,6 +175,13 @@ class Qcow2(Image):
|
||||
disk.extend(qcow2_base, size)
|
||||
libvirt_utils.create_cow_image(qcow2_base, target)
|
||||
|
||||
+ # NOTE(cfb): Having a flavor that sets the root size to 0 and having
|
||||
+ # nova effectively ignore that size and use the size of the
|
||||
+ # image is considered a feature at this time, not a bug.
|
||||
+ if size and size < disk.get_disk_size(base):
|
||||
+ LOG.error('%s virtual size larger than flavor root disk size %s' %
|
||||
+ (base, size))
|
||||
+ raise exception.ImageTooLarge()
|
||||
prepare_template(target=base, *args, **kwargs)
|
||||
with utils.remove_path_on_error(self.path):
|
||||
copy_qcow2_image(base, self.path, size)
|
||||
--
|
||||
1.8.1.5
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
From a4fc0c800502338e4530cad910efb64a5483e1ea Mon Sep 17 00:00:00 2001
|
||||
From: Chet Burgess <cfb@metacloud.com>
|
||||
Date: Thu, 9 May 2013 09:57:28 +0000
|
||||
Subject: [PATCH] Check QCOW2 image size during root disk creation
|
||||
|
||||
glance can only tell us the size of the file, not the virtual
|
||||
size of the QCOW2. As such we need to check the virtual size of
|
||||
the image once its cached and ensure it's <= to the flavor's
|
||||
root disk size.
|
||||
|
||||
Change-Id: I833467284126557eb598b8350a84e10c06292fa9
|
||||
Fixes: bug 1177830
|
||||
(cherry picked from commit 44a8aba1d5da87d54db48079103fdef946666d80)
|
||||
---
|
||||
nova/tests/test_imagebackend.py | 21 +++++++++++++++++++++
|
||||
nova/virt/libvirt/imagebackend.py | 8 ++++++++
|
||||
2 files changed, 29 insertions(+)
|
||||
|
||||
diff --git a/nova/tests/test_imagebackend.py b/nova/tests/test_imagebackend.py
|
||||
index d571bbf..4ec36da 100644
|
||||
--- a/nova/tests/test_imagebackend.py
|
||||
+++ b/nova/tests/test_imagebackend.py
|
||||
@@ -20,6 +20,7 @@ import os
|
||||
import fixtures
|
||||
from oslo.config import cfg
|
||||
|
||||
+from nova import exception
|
||||
from nova.openstack.common import uuidutils
|
||||
from nova import test
|
||||
from nova.tests import fake_libvirt_utils
|
||||
@@ -253,9 +254,12 @@ class Qcow2TestCase(_ImageTestCase, test.TestCase):
|
||||
fn = self.prepare_mocks()
|
||||
fn(target=self.TEMPLATE_PATH)
|
||||
self.mox.StubOutWithMock(os.path, 'exists')
|
||||
+ self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||
if self.OLD_STYLE_INSTANCE_PATH:
|
||||
os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
|
||||
os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
|
||||
+ imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
+ ).AndReturn(self.SIZE)
|
||||
os.path.exists(self.PATH).AndReturn(False)
|
||||
imagebackend.libvirt_utils.create_cow_image(self.TEMPLATE_PATH,
|
||||
self.PATH)
|
||||
@@ -267,6 +271,23 @@ class Qcow2TestCase(_ImageTestCase, test.TestCase):
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
+ def test_create_image_too_small(self):
|
||||
+ fn = self.prepare_mocks()
|
||||
+ fn(target=self.TEMPLATE_PATH)
|
||||
+ self.mox.StubOutWithMock(os.path, 'exists')
|
||||
+ self.mox.StubOutWithMock(imagebackend.disk, 'get_disk_size')
|
||||
+ if self.OLD_STYLE_INSTANCE_PATH:
|
||||
+ os.path.exists(self.OLD_STYLE_INSTANCE_PATH).AndReturn(False)
|
||||
+ os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
|
||||
+ imagebackend.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
+ ).AndReturn(self.SIZE)
|
||||
+ self.mox.ReplayAll()
|
||||
+
|
||||
+ image = self.image_class(self.INSTANCE, self.NAME)
|
||||
+ self.assertRaises(exception.ImageTooLarge, image.create_image, fn,
|
||||
+ self.TEMPLATE_PATH, 1)
|
||||
+ self.mox.VerifyAll()
|
||||
+
|
||||
|
||||
class LvmTestCase(_ImageTestCase, test.TestCase):
|
||||
VG = 'FakeVG'
|
||||
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
|
||||
index b6b1b88..2ca71cc 100755
|
||||
--- a/nova/virt/libvirt/imagebackend.py
|
||||
+++ b/nova/virt/libvirt/imagebackend.py
|
||||
@@ -21,6 +21,7 @@ import os
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
+from nova import exception
|
||||
from nova.openstack.common import excutils
|
||||
from nova.openstack.common import fileutils
|
||||
from nova.openstack.common import lockutils
|
||||
@@ -255,6 +256,13 @@ class Qcow2(Image):
|
||||
|
||||
if not os.path.exists(base):
|
||||
prepare_template(target=base, *args, **kwargs)
|
||||
+ # NOTE(cfb): Having a flavor that sets the root size to 0 and having
|
||||
+ # nova effectively ignore that size and use the size of the
|
||||
+ # image is considered a feature at this time, not a bug.
|
||||
+ if size and size < disk.get_disk_size(base):
|
||||
+ LOG.error('%s virtual size larger than flavor root disk size %s' %
|
||||
+ (base, size))
|
||||
+ raise exception.ImageTooLarge()
|
||||
if not os.path.exists(self.path):
|
||||
with utils.remove_path_on_error(self.path):
|
||||
copy_qcow2_image(base, self.path, size)
|
||||
--
|
||||
1.8.1.5
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#!/sbin/runscript
|
||||
# Copyright 1999-2013 Gentoo Foundation
|
||||
# Distributed under the terms of the GNU General Public License v2
|
||||
|
||||
BASENAME=$(echo $SVCNAME | cut -d '-' -f 1)
|
||||
SERVERNAME=$(echo $SVCNAME | cut -d '-' -f 2)
|
||||
|
||||
depend() {
|
||||
need net
|
||||
}
|
||||
|
||||
checkconfig() {
|
||||
if [ ! -r /etc/conf.d/$BASENAME ]; then
|
||||
eerror "No nova conf.d file found: /etc/conf.d/$BASENAME)"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
start() {
|
||||
checkconfig || return $?
|
||||
. /etc/conf.d/$BASENAME
|
||||
|
||||
ebegin "Starting ${SVCNAME}"
|
||||
if [ ! -d ${PID_PATH} ]; then
|
||||
mkdir ${PID_PATH}
|
||||
chown nova:root ${PID_PATH}
|
||||
fi
|
||||
|
||||
if [ ! -d ${LOCKDIR_PATH} ]; then
|
||||
mkdir ${LOCKDIR_PATH}
|
||||
chown nova:root ${LOCKDIR_PATH}
|
||||
fi
|
||||
|
||||
start-stop-daemon --start \
|
||||
--quiet \
|
||||
--user nova \
|
||||
--pidfile "${PID_PATH}/${SVCNAME}.pid" \
|
||||
--make-pidfile \
|
||||
--background \
|
||||
--exec /usr/bin/nova-${SERVERNAME} -- --config-file /etc/nova/nova.conf
|
||||
|
||||
eend $? "Failed to start ${SVCNAME}"
|
||||
}
|
||||
|
||||
stop() {
|
||||
checkconfig || return $?
|
||||
. /etc/conf.d/$BASENAME
|
||||
|
||||
ebegin "Stopping ${SVCNAME}"
|
||||
|
||||
start-stop-daemon --stop \
|
||||
--quiet \
|
||||
--user nova \
|
||||
--pidfile "${PID_PATH}/${SVCNAME}.pid" \
|
||||
--exec /usr/bin/nova-${SERVERNAME} -- --config-file /etc/nova/nova.conf
|
||||
|
||||
eend $? "Failed to stop ${SVCNAME}"
|
||||
}
|
||||
|
||||
#restart() {
|
||||
#
|
||||
#}
|
|
@ -0,0 +1,3 @@
|
|||
Defaults:nova !requiretty
|
||||
|
||||
nova ALL = (root) NOPASSWD: /usr/bin/nova-rootwrap
|
|
@ -0,0 +1,124 @@
|
|||
# Copyright 1999-2013 Gentoo Foundation
|
||||
# Distributed under the terms of the GNU General Public License v2
|
||||
# $Header: /var/cvsroot/gentoo-x86/sys-cluster/nova/nova-2013.1.3-r6.ebuild,v 1.2 2013/09/27 01:41:21 prometheanfire Exp $
|
||||
|
||||
EAPI=5
|
||||
PYTHON_COMPAT=( python2_7 )
|
||||
|
||||
inherit distutils-r1 eutils multilib
|
||||
|
||||
DESCRIPTION="Nova is a cloud computing fabric controller (main part of an
|
||||
IaaS system). It is written in Python."
|
||||
HOMEPAGE="https://launchpad.net/nova"
|
||||
SRC_URI="http://launchpad.net/${PN}/grizzly/${PV}/+download/${P}.tar.gz"
|
||||
|
||||
LICENSE="Apache-2.0"
|
||||
SLOT="0"
|
||||
KEYWORDS="~amd64 ~x86"
|
||||
IUSE="+api +cert +compute +conductor +consoleauth +kvm +network +novncproxy +scheduler +spicehtml5proxy +xvpvncproxy xen sqlite mysql postgres"
|
||||
REQUIRED_USE="|| ( mysql postgres sqlite )
|
||||
|| ( kvm xen )"
|
||||
|
||||
DEPEND="dev-python/setuptools[${PYTHON_USEDEP}]
|
||||
app-admin/sudo"
|
||||
|
||||
RDEPEND=">=dev-python/amqplib-0.6.1[${PYTHON_USEDEP}]
|
||||
>=dev-python/anyjson-0.2.4[${PYTHON_USEDEP}]
|
||||
>=dev-python/cheetah-2.4.4[${PYTHON_USEDEP}]
|
||||
sqlite? ( >=dev-python/sqlalchemy-0.7.8[sqlite,${PYTHON_USEDEP}]
|
||||
<dev-python/sqlalchemy-0.7.10[sqlite,${PYTHON_USEDEP}] )
|
||||
mysql? ( >=dev-python/sqlalchemy-0.7.8[mysql,${PYTHON_USEDEP}]
|
||||
<dev-python/sqlalchemy-0.7.10[mysql,${PYTHON_USEDEP}] )
|
||||
postgres? ( >=dev-python/sqlalchemy-0.7.8[postgres,${PYTHON_USEDEP}]
|
||||
<dev-python/sqlalchemy-0.7.10[postgres,${PYTHON_USEDEP}] )
|
||||
dev-python/boto[${PYTHON_USEDEP}]
|
||||
>=dev-python/lxml-2.3[${PYTHON_USEDEP}]
|
||||
>=dev-python/eventlet-0.9.17[${PYTHON_USEDEP}]
|
||||
>=dev-python/kombu-1.0.4-r1[${PYTHON_USEDEP}]
|
||||
>=dev-python/routes-1.12.3-r1[${PYTHON_USEDEP}]
|
||||
~dev-python/webob-1.2.3[${PYTHON_USEDEP}]
|
||||
>=dev-python/greenlet-0.3.1[${PYTHON_USEDEP}]
|
||||
>=dev-python/pastedeploy-1.5.0-r1[${PYTHON_USEDEP}]
|
||||
dev-python/paste[${PYTHON_USEDEP}]
|
||||
>=dev-python/sqlalchemy-migrate-0.7.2[${PYTHON_USEDEP}]
|
||||
dev-python/netaddr[${PYTHON_USEDEP}]
|
||||
>=dev-python/suds-0.4[${PYTHON_USEDEP}]
|
||||
dev-python/paramiko[${PYTHON_USEDEP}]
|
||||
dev-python/pyasn1[${PYTHON_USEDEP}]
|
||||
>=dev-python/Babel-0.9.6[${PYTHON_USEDEP}]
|
||||
>=dev-python/iso8601-0.1.4[${PYTHON_USEDEP}]
|
||||
dev-python/httplib2[${PYTHON_USEDEP}]
|
||||
>=dev-python/setuptools-git-0.4[${PYTHON_USEDEP}]
|
||||
>=dev-python/python-cinderclient-1.0.1[${PYTHON_USEDEP}]
|
||||
>=dev-python/python-glanceclient-0.5.0[${PYTHON_USEDEP}]
|
||||
<dev-python/python-glanceclient-2[${PYTHON_USEDEP}]
|
||||
>=dev-python/python-neutronclient-2.2.0[${PYTHON_USEDEP}]
|
||||
<=dev-python/python-neutronclient-3.0.0[${PYTHON_USEDEP}]
|
||||
>=dev-python/python-keystoneclient-0.2.0[${PYTHON_USEDEP}]
|
||||
>=dev-python/stevedore-0.7[${PYTHON_USEDEP}]
|
||||
<dev-python/websockify-0.4[${PYTHON_USEDEP}]
|
||||
>=dev-python/oslo-config-1.1.0[${PYTHON_USEDEP}]
|
||||
<dev-python/oslo-config-1.2.0[${PYTHON_USEDEP}]
|
||||
virtual/python-argparse[${PYTHON_USEDEP}]
|
||||
app-emulation/libvirt
|
||||
novncproxy? ( www-apps/novnc )
|
||||
kvm? ( app-emulation/qemu )
|
||||
xen? ( app-emulation/xen
|
||||
app-emulation/xen-tools )"
|
||||
|
||||
PATCHES=(
|
||||
"${FILESDIR}/2013.1.3-CVE-2013-4261.patch"
|
||||
"${FILESDIR}/2013.1.3-CVE-2013-4278.patch"
|
||||
)
|
||||
|
||||
pkg_setup() {
|
||||
enewgroup nova
|
||||
enewuser nova -1 -1 /var/lib/nova nova
|
||||
}
|
||||
|
||||
python_install() {
|
||||
distutils-r1_python_install
|
||||
newconfd "${FILESDIR}/nova-confd" "nova"
|
||||
newinitd "${FILESDIR}/nova-initd" "nova"
|
||||
use api && dosym /etc/init.d/nova /etc/init.d/nova-api
|
||||
use cert && dosym /etc/init.d/nova /etc/init.d/nova-cert
|
||||
use compute && dosym /etc/init.d/nova /etc/init.d/nova-compute
|
||||
use conductor && dosym /etc/init.d/nova /etc/init.d/nova-conductor
|
||||
use consoleauth && dosym /etc/init.d/nova /etc/init.d/nova-consoleauth
|
||||
use network && dosym /etc/init.d/nova /etc/init.d/nova-network
|
||||
use novncproxy &&dosym /etc/init.d/nova /etc/init.d/nova-novncproxy
|
||||
use scheduler && dosym /etc/init.d/nova /etc/init.d/nova-scheduler
|
||||
use spicehtml5proxy && dosym /etc/init.d/nova /etc/init.d/nova-spicehtml5proxy
|
||||
use xvpvncproxy && dosym /etc/init.d/nova /etc/init.d/nova-xvpncproxy
|
||||
|
||||
diropts -m 0750
|
||||
dodir /var/run/nova /var/log/nova /var/lock/nova
|
||||
fowners nova:nova /var/log/nova /var/lock/nova /var/run/nova
|
||||
|
||||
diropts -m 0755
|
||||
dodir /var/lib/nova/instances
|
||||
fowners nova:nova /var/lib/nova/instances
|
||||
|
||||
keepdir /etc/nova
|
||||
insinto /etc/nova
|
||||
newins "etc/nova/nova.conf.sample" "nova.conf"
|
||||
doins "etc/nova/api-paste.ini"
|
||||
doins "etc/nova/logging_sample.conf"
|
||||
doins "etc/nova/policy.json"
|
||||
doins "etc/nova/rootwrap.conf"
|
||||
insinto /etc/nova/rootwrap.d
|
||||
doins "etc/nova/rootwrap.d/api-metadata.filters"
|
||||
doins "etc/nova/rootwrap.d/compute.filters"
|
||||
doins "etc/nova/rootwrap.d/network.filters"
|
||||
|
||||
#copy migration conf file (not coppied on install via setup.py script)
|
||||
insinto /usr/$(get_libdir)/python2.7/site-packages/nova/db/sqlalchemy/migrate_repo/
|
||||
doins "nova/db/sqlalchemy/migrate_repo/migrate.cfg"
|
||||
|
||||
#copy the CA cert dir (not coppied on install via setup.py script)
|
||||
cp -R "${S}/nova/CA" "${D}/usr/$(get_libdir)/python2.7/site-packages/nova/" || die "isntalling CA files failed"
|
||||
|
||||
#add sudoers definitions for user nova
|
||||
insinto /etc/sudoers.d/
|
||||
doins "${FILESDIR}/nova-sudoers"
|
||||
}
|
Loading…
Reference in New Issue