From d764a483497afc5d029a82db14cc5cc88f45f4c0 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Thu, 9 Jun 2011 19:43:48 +0000 Subject: Add an extension to allow for an addFixedIp action on instances --- nova/api/openstack/contrib/multinic.py | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 nova/api/openstack/contrib/multinic.py diff --git a/nova/api/openstack/contrib/multinic.py b/nova/api/openstack/contrib/multinic.py new file mode 100644 index 000000000..164af79b0 --- /dev/null +++ b/nova/api/openstack/contrib/multinic.py @@ -0,0 +1,83 @@ +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""The multinic extension.""" + +from webob import exc + +from nova import compute +from nova import log as logging +from nova.api.openstack import extensions +from nova.api.openstack import faults + + +LOG = logging.getLogger("nova.api.multinic") + + +class Multinic(extensions.ExtensionDescriptor): + def __init__(self, *args, **kwargs): + super(Multinic, self).__init__(*args, **kwargs) + self.compute_api = compute.API() + + def get_name(self): + return "Multinic" + + def get_alias(self): + return "NMN" + + def get_description(self): + return "Multiple network support" + + def get_namespace(self): + return "http://docs.openstack.org/ext/multinic/api/v1.1" + + def get_updated(self): + return "2011-06-09T00:00:00+00:00" + + def get_actions(self): + actions = [] + + # Add the add_fixed_ip action + act = extensions.ActionExtension("servers", "addFixedIp", + self._add_fixed_ip) + actions.append(act) + + # Add the remove_fixed_ip action + act = extensions.ActionExtension("servers", "removeFixedIp", + self._remove_fixed_ip) + actions.append(act) + + return actions + + def _add_fixed_ip(self, input_dict, req, id): + """Adds an IP on a given network to an instance.""" + try: + # Validate the input entity + if 'networkId' not in input_dict['addFixedIp']: + LOG.exception(_("Missing 'networkId' argument for addFixedIp")) + return faults.Fault(exc.HTTPUnprocessableEntity()) + + # Add the fixed IP + network_id = input_dict['addFixedIp']['networkId'] + self.compute_api.add_fixed_ip(req.environ['nova.context'], id, + network_id) + except Exception, e: + LOG.exception(_("Error in addFixedIp %s"), e) + return faults.Fault(exc.HTTPBadRequest()) + return exc.HTTPAccepted() + + def _remove_fixed_ip(self, input_dict, req, id): + # Not yet implemented + raise faults.Fault(exc.HTTPNotImplemented()) -- cgit From 6d1ce4a46a9684ac718bfc19dc8a362fbc9a9641 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 27 Jun 2011 09:02:22 -0700 Subject: remove extra stuff from clean vlans --- tools/clean-vlans | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/clean-vlans b/tools/clean-vlans index 820a9dbe5..a26ad86ad 100755 --- a/tools/clean-vlans +++ b/tools/clean-vlans @@ -17,9 +17,9 @@ # License for the specific language governing permissions and limitations # under the License. -export LC_ALL=C +export LC_ALL=C sudo ifconfig -a | grep br | grep -v bridge | cut -f1 -d" " | xargs -n1 -ifoo ifconfig foo down sudo ifconfig -a | grep br | grep -v bridge | cut -f1 -d" " | xargs -n1 -ifoo brctl delbr foo -sudo ifconfig -a | grep vlan | grep -v vlan124 | grep -v vlan5 | cut -f1 -d" " | xargs -n1 -ifoo ifconfig foo down -sudo ifconfig -a | grep vlan | grep -v vlan124 | grep -v vlan5 | cut -f1 -d" " | xargs -n1 -ifoo vconfig rem foo +sudo ifconfig -a | grep vlan | cut -f1 -d" " | xargs -n1 -ifoo ifconfig foo down +sudo ifconfig -a | grep vlan | cut -f1 -d" " | xargs -n1 -ifoo vconfig rem foo -- cgit From 7230c384d1373e8718bc7fd2fd86b1e805a0573e Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 27 Jun 2011 09:05:05 -0700 Subject: make nova-debug work with new style instances --- tools/nova-debug | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tools/nova-debug b/tools/nova-debug index 3ff68ca35..fd9cb054a 100755 --- a/tools/nova-debug +++ b/tools/nova-debug @@ -1,4 +1,3 @@ -#!/usr/bin/env bash # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the @@ -30,13 +29,15 @@ cd $INSTANCES_PATH/$1 if [ $CMD != "umount" ] && [ $CMD != "launch" ]; then # destroy the instance virsh destroy $1 +virsh undefine $1 # mount the filesystem mkdir t -DEVICE=`losetup --show -f disk` +DEVICE=/dev/nbd0 echo $DEVICE -kpartx -a $DEVICE -mount /dev/mapper/${DEVICE:4}p1 t +qemu-nbd -c $DEVICE disk +sleep 3 +mount $DEVICE t fi if [ $CMD != "mount" ] && [ $CMD != "umount" ]; then @@ -66,11 +67,13 @@ sed -i "s/.*<\/serial>/ Date: Mon, 27 Jun 2011 09:07:49 -0700 Subject: update nova.sh --- contrib/nova.sh | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/contrib/nova.sh b/contrib/nova.sh index d7d34dcbd..eab680580 100755 --- a/contrib/nova.sh +++ b/contrib/nova.sh @@ -17,7 +17,7 @@ if [ ! -n "$HOST_IP" ]; then HOST_IP=`LC_ALL=C ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` fi -USE_MYSQL=${USE_MYSQL:-0} +USE_MYSQL=${USE_MYSQL:-1} INTERFACE=${INTERFACE:-eth0} FLOATING_RANGE=${FLOATING_RANGE:-10.6.0.0/27} FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24} @@ -159,10 +159,6 @@ NOVA_CONF_EOF mkdir -p $NOVA_DIR/instances rm -rf $NOVA_DIR/networks mkdir -p $NOVA_DIR/networks - if [ ! -d "$NOVA_DIR/images" ]; then - ln -s $DIR/images $NOVA_DIR/images - fi - if [ "$TEST" == 1 ]; then cd $NOVA_DIR python $NOVA_DIR/run_tests.py @@ -181,8 +177,18 @@ NOVA_CONF_EOF # create some floating ips $NOVA_DIR/bin/nova-manage floating create `hostname` $FLOATING_RANGE - # convert old images - $NOVA_DIR/bin/nova-manage image convert $DIR/images + if [ ! -d "$NOVA_DIR/images" ]; then + if [ ! -d "$DIR/converted-images" ]; then + # convert old images + mkdir $DIR/converted-images + ln -s $DIR/converted-images $NOVA_DIR/images + $NOVA_DIR/bin/nova-manage image convert $DIR/images + else + ln -s $DIR/converted-images $NOVA_DIR/images + fi + + fi + # nova api crashes if we start it with a regular screen command, # so send the start command by forcing text into the window. -- cgit From b245dfd693107318c90d1af4fdf057ea1a5fc5c2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 27 Jun 2011 09:17:37 -0700 Subject: missed the bin line --- tools/nova-debug | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/nova-debug b/tools/nova-debug index fd9cb054a..0a78af16a 100755 --- a/tools/nova-debug +++ b/tools/nova-debug @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the -- cgit From 302d5ad4caaef4f02f475c2e9abd782ac630beb8 Mon Sep 17 00:00:00 2001 From: Joseph Suh Date: Thu, 30 Jun 2011 14:24:27 -0400 Subject: fix bug 800759 --- nova/scheduler/zone_manager.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index ba7403c15..e0daa01e2 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -109,6 +109,7 @@ class ZoneManager(object): self.last_zone_db_check = datetime.datetime.min self.zone_states = {} # { : ZoneState } self.service_states = {} # { : { : { cap k : v }}} + self.service_time_stamp = {} # reported time self.green_pool = greenpool.GreenPool() def get_zone_list(self): @@ -125,14 +126,18 @@ class ZoneManager(object): # But it's likely to change once we understand what the Best-Match # code will need better. combined = {} # { _ : (min, max), ... } + allowed_time_diff = FLAGS.periodic_interval * 3 for host, host_dict in hosts_dict.iteritems(): - for service_name, service_dict in host_dict.iteritems(): - for cap, value in service_dict.iteritems(): - key = "%s_%s" % (service_name, cap) - min_value, max_value = combined.get(key, (value, value)) - min_value = min(min_value, value) - max_value = max(max_value, value) - combined[key] = (min_value, max_value) + if (utils.utcnow() - self.service_time_stamp[host]) <= \ + datetime.timedelta(seconds=allowed_time_diff): + for service_name, service_dict in host_dict.iteritems(): + for cap, value in service_dict.iteritems(): + key = "%s_%s" % (service_name, cap) + min_value, max_value = combined.get(key, \ + (value, value)) + min_value = min(min_value, value) + max_value = max(max_value, value) + combined[key] = (min_value, max_value) return combined @@ -174,3 +179,4 @@ class ZoneManager(object): service_caps = self.service_states.get(host, {}) service_caps[service_name] = capabilities self.service_states[host] = service_caps + self.service_time_stamp[host] = utils.utcnow() -- cgit From 7048530fb6083ab92fa6b7a2b5a73fc2bf30709f Mon Sep 17 00:00:00 2001 From: Devendra Modium Date: Thu, 7 Jul 2011 11:25:45 -0400 Subject: Comments Incorporated for Bug800759 --- Authors | 1 + nova/scheduler/zone_manager.py | 55 ++++++++++++++----- nova/tests/test_zones.py | 121 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 14 deletions(-) diff --git a/Authors b/Authors index c3a65f1b4..67cd38bcf 100644 --- a/Authors +++ b/Authors @@ -93,3 +93,4 @@ Yoshiaki Tamura Youcef Laribi Yuriy Taraday Zhixue Wu +Devendra Modium diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index e0daa01e2..2a0816118 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -109,7 +109,6 @@ class ZoneManager(object): self.last_zone_db_check = datetime.datetime.min self.zone_states = {} # { : ZoneState } self.service_states = {} # { : { : { cap k : v }}} - self.service_time_stamp = {} # reported time self.green_pool = greenpool.GreenPool() def get_zone_list(self): @@ -126,19 +125,28 @@ class ZoneManager(object): # But it's likely to change once we understand what the Best-Match # code will need better. combined = {} # { _ : (min, max), ... } - allowed_time_diff = FLAGS.periodic_interval * 3 + stale_host_services = {} # { host1 : [svc1, svc2], host2 :[svc1]} for host, host_dict in hosts_dict.iteritems(): - if (utils.utcnow() - self.service_time_stamp[host]) <= \ - datetime.timedelta(seconds=allowed_time_diff): - for service_name, service_dict in host_dict.iteritems(): - for cap, value in service_dict.iteritems(): - key = "%s_%s" % (service_name, cap) - min_value, max_value = combined.get(key, \ - (value, value)) - min_value = min(min_value, value) - max_value = max(max_value, value) - combined[key] = (min_value, max_value) - + for service_name, service_dict in host_dict.iteritems(): + + #Check if the service capabilities became stale + if self.host_service_caps_stale(host, service_name): + if host not in stale_host_services: + stale_host_services[host] = [] # Adding host key once + stale_host_services[host].append(service_name) + continue + for cap, value in service_dict.iteritems(): + if cap == "timestamp": # Timestamp is not needed + continue + key = "%s_%s" % (service_name, cap) + min_value, max_value = combined.get(key, \ + (value, value)) + min_value = min(min_value, value) + max_value = max(max_value, value) + combined[key] = (min_value, max_value) + + # Delete the expired host services + self.delete_expired_host_services(stale_host_services) return combined def _refresh_from_db(self, context): @@ -177,6 +185,25 @@ class ZoneManager(object): logging.debug(_("Received %(service_name)s service update from " "%(host)s: %(capabilities)s") % locals()) service_caps = self.service_states.get(host, {}) + capabilities["timestamp"] = utils.utcnow() # Reported time service_caps[service_name] = capabilities self.service_states[host] = service_caps - self.service_time_stamp[host] = utils.utcnow() + + def host_service_caps_stale(self, host, service): + """Check if host service capabilites are not recent enough.""" + allowed_time_diff = FLAGS.periodic_interval * 3 + caps = self.service_states[host][service] + if (utils.utcnow() - caps["timestamp"]) <= \ + datetime.timedelta(seconds=allowed_time_diff): + return False + return True + + def delete_expired_host_services(self, host_services_dict): + """Delete all the inactive host services information.""" + for host, services in host_services_dict.iteritems(): + service_caps = self.service_states[host] + for service in services: + del service_caps[service] + if len(service_caps) == 0: # Delete host if no services + del self.service_states[host] + diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index e132809dc..f4661a328 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -198,3 +198,124 @@ class ZoneManagerTestCase(test.TestCase): self.assertEquals(zone_state.attempt, 3) self.assertFalse(zone_state.is_active) self.assertEquals(zone_state.name, None) + + def test_host_service_caps_stale(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + + # services just updated capabilities + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc2", "host1", dict(a=3, b=4)) + self.assertFalse(zm.host_service_caps_stale("host1", "svc1")) + self.assertFalse(zm.host_service_caps_stale("host1", "svc2")) + + # Both services became stale + time_future = utils.utcnow() + datetime.timedelta(seconds=expiry_time) + utils.set_time_override(time_future) + self.assertTrue(zm.host_service_caps_stale("host1", "svc1")) + self.assertTrue(zm.host_service_caps_stale("host1", "svc2")) + + # One service became stale + utils.clear_time_override() + caps = zm.service_states["host1"]["svc1"] + caps["timestamp"] = utils.utcnow() - \ + datetime.timedelta(seconds=expiry_time) + self.assertTrue(zm.host_service_caps_stale("host1", "svc1")) + self.assertFalse(zm.host_service_caps_stale("host1", "svc2")) + + def test_delete_expired_host_services(self): + zm = zone_manager.ZoneManager() + + # Delete one service in a host + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc2", "host1", dict(a=3, b=4)) + stale_host_services = {"host1": ["svc1"]} + zm.delete_expired_host_services(stale_host_services) + self.assertFalse("svc1" in zm.service_states["host1"]) + self.assertTrue("svc2" in zm.service_states["host1"]) + + # Delete all services in a host + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + stale_host_services = {"host1": ["svc1", "svc2"]} + zm.delete_expired_host_services(stale_host_services) + self.assertFalse("host1" in zm.service_states) + + # Delete one service per host + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) + stale_host_services = {"host1": ["svc1"], "host2": ["svc1"]} + zm.delete_expired_host_services(stale_host_services) + self.assertFalse("host1" in zm.service_states) + self.assertFalse("host2" in zm.service_states) + + def test_get_zone_capabilities(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + + # Service capabilities recent + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, dict(svc1_a=(1, 1), svc1_b=(2, 2))) + + # Service capabilities stale + time_future = utils.utcnow() + datetime.timedelta(seconds=expiry_time) + utils.set_time_override(time_future) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, {}) + + # Both host service capabilities recent + utils.clear_time_override() + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, dict(svc1_a=(1, 3), svc1_b=(2, 4))) + + # One host service capabilities become stale + serv_caps = zm.service_states["host1"]["svc1"] + serv_caps["timestamp"] = utils.utcnow() - \ + datetime.timedelta(seconds=expiry_time) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, dict(svc1_a=(3, 3), svc1_b=(4, 4))) + + # Multiple services per host + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) + zm.update_service_capabilities("svc2", "host2", dict(a=7, b=8)) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, dict(svc1_a=(1, 3), svc1_b=(2, 4), + svc2_a=(5, 7), svc2_b=(6, 8))) + + # Two host services among four become stale + serv_caps_1 = zm.service_states["host1"]["svc2"] + serv_caps_1["timestamp"] = utils.utcnow() - \ + datetime.timedelta(seconds=expiry_time) + serv_caps_2 = zm.service_states["host2"]["svc1"] + serv_caps_2["timestamp"] = utils.utcnow() - \ + datetime.timedelta(seconds=expiry_time) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, dict(svc1_a=(1, 1), svc1_b=(2, 2), + svc2_a=(7, 7), svc2_b=(8, 8))) + + # Three host services among four become stale + zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) + zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) + serv_caps_1 = zm.service_states["host1"]["svc2"] + serv_caps_1["timestamp"] = utils.utcnow() - \ + datetime.timedelta(seconds=expiry_time) + serv_caps_2 = zm.service_states["host2"]["svc1"] + serv_caps_2["timestamp"] = utils.utcnow() - \ + datetime.timedelta(seconds=expiry_time) + serv_caps_3 = zm.service_states["host2"]["svc2"] + serv_caps_3["timestamp"] = utils.utcnow() - \ + datetime.timedelta(seconds=expiry_time) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, dict(svc1_a=(1, 1), svc1_b=(2, 2))) + + # All the host services become stale + zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) + zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) + zm.update_service_capabilities("svc2", "host2", dict(a=7, b=8)) + utils.set_time_override(time_future) + caps = zm.get_zone_capabilities(None) + self.assertEquals(caps, {}) + -- cgit From 784c0a76967310843269d7f8be9df6bc292ff3c8 Mon Sep 17 00:00:00 2001 From: Devendra Modium Date: Thu, 7 Jul 2011 15:55:34 -0400 Subject: Comments for bugfix800759 and pep8 --- nova/scheduler/zone_manager.py | 12 +++--- nova/tests/test_zones.py | 88 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/nova/scheduler/zone_manager.py b/nova/scheduler/zone_manager.py index 2a0816118..8f928ce9e 100644 --- a/nova/scheduler/zone_manager.py +++ b/nova/scheduler/zone_manager.py @@ -125,7 +125,7 @@ class ZoneManager(object): # But it's likely to change once we understand what the Best-Match # code will need better. combined = {} # { _ : (min, max), ... } - stale_host_services = {} # { host1 : [svc1, svc2], host2 :[svc1]} + stale_host_services = {} # { host1 : [svc1, svc2], host2 :[svc1]} for host, host_dict in hosts_dict.iteritems(): for service_name, service_dict in host_dict.iteritems(): @@ -136,11 +136,10 @@ class ZoneManager(object): stale_host_services[host].append(service_name) continue for cap, value in service_dict.iteritems(): - if cap == "timestamp": # Timestamp is not needed + if cap == "timestamp": # Timestamp is not needed continue key = "%s_%s" % (service_name, cap) - min_value, max_value = combined.get(key, \ - (value, value)) + min_value, max_value = combined.get(key, (value, value)) min_value = min(min_value, value) max_value = max(max_value, value) combined[key] = (min_value, max_value) @@ -185,7 +184,7 @@ class ZoneManager(object): logging.debug(_("Received %(service_name)s service update from " "%(host)s: %(capabilities)s") % locals()) service_caps = self.service_states.get(host, {}) - capabilities["timestamp"] = utils.utcnow() # Reported time + capabilities["timestamp"] = utils.utcnow() # Reported time service_caps[service_name] = capabilities self.service_states[host] = service_caps @@ -204,6 +203,5 @@ class ZoneManager(object): service_caps = self.service_states[host] for service in services: del service_caps[service] - if len(service_caps) == 0: # Delete host if no services + if len(service_caps) == 0: # Delete host if no services del self.service_states[host] - diff --git a/nova/tests/test_zones.py b/nova/tests/test_zones.py index f4661a328..a943fee27 100644 --- a/nova/tests/test_zones.py +++ b/nova/tests/test_zones.py @@ -199,31 +199,42 @@ class ZoneManagerTestCase(test.TestCase): self.assertFalse(zone_state.is_active) self.assertEquals(zone_state.name, None) - def test_host_service_caps_stale(self): + def test_host_service_caps_stale_no_stale_service(self): zm = zone_manager.ZoneManager() - expiry_time = (FLAGS.periodic_interval * 3) + 1 - # services just updated capabilities + # services just updated capabilities zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) zm.update_service_capabilities("svc2", "host1", dict(a=3, b=4)) self.assertFalse(zm.host_service_caps_stale("host1", "svc1")) self.assertFalse(zm.host_service_caps_stale("host1", "svc2")) + def test_host_service_caps_stale_all_stale_services(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + # Both services became stale + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc2", "host1", dict(a=3, b=4)) time_future = utils.utcnow() + datetime.timedelta(seconds=expiry_time) utils.set_time_override(time_future) self.assertTrue(zm.host_service_caps_stale("host1", "svc1")) self.assertTrue(zm.host_service_caps_stale("host1", "svc2")) + utils.clear_time_override() + + def test_host_service_caps_stale_one_stale_service(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 # One service became stale - utils.clear_time_override() + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc2", "host1", dict(a=3, b=4)) caps = zm.service_states["host1"]["svc1"] caps["timestamp"] = utils.utcnow() - \ datetime.timedelta(seconds=expiry_time) self.assertTrue(zm.host_service_caps_stale("host1", "svc1")) self.assertFalse(zm.host_service_caps_stale("host1", "svc2")) - - def test_delete_expired_host_services(self): + + def test_delete_expired_host_services_del_one_service(self): zm = zone_manager.ZoneManager() # Delete one service in a host @@ -234,12 +245,19 @@ class ZoneManagerTestCase(test.TestCase): self.assertFalse("svc1" in zm.service_states["host1"]) self.assertTrue("svc2" in zm.service_states["host1"]) + def test_delete_expired_host_services_del_all_hosts(self): + zm = zone_manager.ZoneManager() + # Delete all services in a host + zm.update_service_capabilities("svc2", "host1", dict(a=3, b=4)) zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) stale_host_services = {"host1": ["svc1", "svc2"]} zm.delete_expired_host_services(stale_host_services) self.assertFalse("host1" in zm.service_states) + def test_delete_expired_host_services_del_one_service_per_host(self): + zm = zone_manager.ZoneManager() + # Delete one service per host zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) @@ -247,45 +265,70 @@ class ZoneManagerTestCase(test.TestCase): zm.delete_expired_host_services(stale_host_services) self.assertFalse("host1" in zm.service_states) self.assertFalse("host2" in zm.service_states) - - def test_get_zone_capabilities(self): + + def test_get_zone_capabilities_one_host(self): zm = zone_manager.ZoneManager() - expiry_time = (FLAGS.periodic_interval * 3) + 1 # Service capabilities recent zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) caps = zm.get_zone_capabilities(None) self.assertEquals(caps, dict(svc1_a=(1, 1), svc1_b=(2, 2))) + def test_get_zone_capabilities_expired_host(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + # Service capabilities stale + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) time_future = utils.utcnow() + datetime.timedelta(seconds=expiry_time) utils.set_time_override(time_future) caps = zm.get_zone_capabilities(None) self.assertEquals(caps, {}) + utils.clear_time_override() + + def test_get_zone_capabilities_multiple_hosts(self): + zm = zone_manager.ZoneManager() # Both host service capabilities recent - utils.clear_time_override() zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) caps = zm.get_zone_capabilities(None) self.assertEquals(caps, dict(svc1_a=(1, 3), svc1_b=(2, 4))) + def test_get_zone_capabilities_one_stale_host(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + # One host service capabilities become stale + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) serv_caps = zm.service_states["host1"]["svc1"] serv_caps["timestamp"] = utils.utcnow() - \ datetime.timedelta(seconds=expiry_time) caps = zm.get_zone_capabilities(None) self.assertEquals(caps, dict(svc1_a=(3, 3), svc1_b=(4, 4))) - # Multiple services per host + def test_get_zone_capabilities_multiple_service_per_host(self): + zm = zone_manager.ZoneManager() + + # Multiple services per host zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) zm.update_service_capabilities("svc2", "host2", dict(a=7, b=8)) caps = zm.get_zone_capabilities(None) self.assertEquals(caps, dict(svc1_a=(1, 3), svc1_b=(2, 4), svc2_a=(5, 7), svc2_b=(6, 8))) - # Two host services among four become stale + def test_get_zone_capabilities_one_stale_service_per_host(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + + # Two host services among four become stale + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) + zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) + zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) + zm.update_service_capabilities("svc2", "host2", dict(a=7, b=8)) serv_caps_1 = zm.service_states["host1"]["svc2"] serv_caps_1["timestamp"] = utils.utcnow() - \ datetime.timedelta(seconds=expiry_time) @@ -296,9 +339,15 @@ class ZoneManagerTestCase(test.TestCase): self.assertEquals(caps, dict(svc1_a=(1, 1), svc1_b=(2, 2), svc2_a=(7, 7), svc2_b=(8, 8))) - # Three host services among four become stale - zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) + def test_get_zone_capabilities_three_stale_host_services(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + + # Three host services among four become stale + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) + zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) + zm.update_service_capabilities("svc2", "host2", dict(a=7, b=8)) serv_caps_1 = zm.service_states["host1"]["svc2"] serv_caps_1["timestamp"] = utils.utcnow() - \ datetime.timedelta(seconds=expiry_time) @@ -311,11 +360,16 @@ class ZoneManagerTestCase(test.TestCase): caps = zm.get_zone_capabilities(None) self.assertEquals(caps, dict(svc1_a=(1, 1), svc1_b=(2, 2))) - # All the host services become stale - zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) + def test_get_zone_capabilities_all_stale_host_services(self): + zm = zone_manager.ZoneManager() + expiry_time = (FLAGS.periodic_interval * 3) + 1 + + # All the host services become stale + zm.update_service_capabilities("svc1", "host1", dict(a=1, b=2)) zm.update_service_capabilities("svc1", "host2", dict(a=3, b=4)) + zm.update_service_capabilities("svc2", "host1", dict(a=5, b=6)) zm.update_service_capabilities("svc2", "host2", dict(a=7, b=8)) + time_future = utils.utcnow() + datetime.timedelta(seconds=expiry_time) utils.set_time_override(time_future) caps = zm.get_zone_capabilities(None) self.assertEquals(caps, {}) - -- cgit From 49697c5fa1ce3d4b1ae366baeda3336f5edab5a8 Mon Sep 17 00:00:00 2001 From: Devendra Modium Date: Thu, 7 Jul 2011 16:47:23 -0400 Subject: Merging issues --- Authors | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authors b/Authors index 67cd38bcf..052f2d964 100644 --- a/Authors +++ b/Authors @@ -20,6 +20,7 @@ Dan Prince Dave Walker David Pravec Dean Troyer +Devendra Modium Devin Carlen Ed Leafe Eldar Nugaev @@ -93,4 +94,3 @@ Yoshiaki Tamura Youcef Laribi Yuriy Taraday Zhixue Wu -Devendra Modium -- cgit From 9f3a309eab3af46aa3f8ac2767b7edcdeb49e043 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Thu, 7 Jul 2011 16:46:55 -0500 Subject: unit test suite for the multinic extension --- .../api/openstack/contrib/test_multinic_xs.py | 88 ++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 nova/tests/api/openstack/contrib/test_multinic_xs.py diff --git a/nova/tests/api/openstack/contrib/test_multinic_xs.py b/nova/tests/api/openstack/contrib/test_multinic_xs.py new file mode 100644 index 000000000..e531435a0 --- /dev/null +++ b/nova/tests/api/openstack/contrib/test_multinic_xs.py @@ -0,0 +1,88 @@ +# Copyright 2011 Eldar Nugaev +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import stubout +import webob + +from nova import compute +from nova import context +from nova import test +from nova.tests.api.openstack import fakes + + +last_add_fixed_ip = (None, None) + + +def compute_api_add_fixed_ip(self, context, instance_id, network_id): + global last_add_fixed_ip + + last_add_fixed_ip = (instance_id, network_id) + + +class FixedIpTest(test.TestCase): + def setUp(self): + super(FixedIpTest, self).setUp() + self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.reset_fake_data() + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_networking(self.stubs) + fakes.stub_out_rate_limiting(self.stubs) + fakes.stub_out_auth(self.stubs) + self.stubs.Set(compute.api.API, "add_fixed_ip", + compute_api_add_fixed_ip) + self.context = context.get_admin_context() + + def tearDown(self): + self.stubs.UnsetAll() + super(FixedIpTest, self).tearDown() + + def test_add_fixed_ip(self): + global last_add_fixed_ip + last_add_fixed_ip = (None, None) + + body = dict(addFixedIp=dict(networkId='test_net')) + req = webob.Request.blank('/v1.1/servers/test_inst/action') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 202) + self.assertNotEqual(last_add_fixed_ip, (None, None)) + + def test_add_fixed_ip_no_network(self): + global last_add_fixed_ip + last_add_fixed_ip = (None, None) + + body = dict(addFixedIp=dict()) + req = webob.Request.blank('/v1.1/servers/test_inst/action') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 422) + self.assertEqual(last_add_fixed_ip, (None, None)) + + def test_remove_fixed_ip(self): + body = dict(removeFixedIp=dict(address='10.10.10.1')) + req = webob.Request.blank('/v1.1/servers/test_inst/action') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 501) -- cgit From 921fee22ff42852b1ee0d7f3d051b44d60afd975 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Fri, 8 Jul 2011 13:06:37 -0500 Subject: Add support for remove_fixed_ip() --- nova/api/openstack/contrib/multinic.py | 20 +++++++++++-- .../api/openstack/contrib/test_multinic_xs.py | 35 ++++++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/contrib/multinic.py b/nova/api/openstack/contrib/multinic.py index 164af79b0..e202bd51c 100644 --- a/nova/api/openstack/contrib/multinic.py +++ b/nova/api/openstack/contrib/multinic.py @@ -26,6 +26,8 @@ from nova.api.openstack import faults LOG = logging.getLogger("nova.api.multinic") +# Note: The class name is as it has to be for this to be loaded as an +# extension--only first character capitalized. class Multinic(extensions.ExtensionDescriptor): def __init__(self, *args, **kwargs): super(Multinic, self).__init__(*args, **kwargs) @@ -79,5 +81,19 @@ class Multinic(extensions.ExtensionDescriptor): return exc.HTTPAccepted() def _remove_fixed_ip(self, input_dict, req, id): - # Not yet implemented - raise faults.Fault(exc.HTTPNotImplemented()) + """Removes an IP from an instance.""" + try: + # Validate the input entity + if 'address' not in input_dict['removeFixedIp']: + LOG.exception(_("Missing 'address' argument for " + "removeFixedIp")) + return faults.Fault(exc.HTTPUnprocessableEntity()) + + # Remove the fixed IP + address = input_dict['removeFixedIp']['address'] + self.compute_api.remove_fixed_ip(req.environ['nova.context'], id, + address) + except Exception, e: + LOG.exception(_("Error in removeFixedIp %s"), e) + return faults.Fault(exc.HTTPBadRequest()) + return exc.HTTPAccepted() diff --git a/nova/tests/api/openstack/contrib/test_multinic_xs.py b/nova/tests/api/openstack/contrib/test_multinic_xs.py index e531435a0..b9f4456bb 100644 --- a/nova/tests/api/openstack/contrib/test_multinic_xs.py +++ b/nova/tests/api/openstack/contrib/test_multinic_xs.py @@ -1,4 +1,4 @@ -# Copyright 2011 Eldar Nugaev +# Copyright 2011 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -24,6 +24,7 @@ from nova.tests.api.openstack import fakes last_add_fixed_ip = (None, None) +last_remove_fixed_ip = (None, None) def compute_api_add_fixed_ip(self, context, instance_id, network_id): @@ -32,6 +33,12 @@ def compute_api_add_fixed_ip(self, context, instance_id, network_id): last_add_fixed_ip = (instance_id, network_id) +def compute_api_remove_fixed_ip(self, context, instance_id, address): + global last_remove_fixed_ip + + last_remove_fixed_ip = (instance_id, address) + + class FixedIpTest(test.TestCase): def setUp(self): super(FixedIpTest, self).setUp() @@ -43,6 +50,9 @@ class FixedIpTest(test.TestCase): fakes.stub_out_auth(self.stubs) self.stubs.Set(compute.api.API, "add_fixed_ip", compute_api_add_fixed_ip) + # TODO(Vek): Fails until remove_fixed_ip() added + # self.stubs.Set(compute.api.API, "remove_fixed_ip", + # compute_api_remove_fixed_ip) self.context = context.get_admin_context() def tearDown(self): @@ -61,7 +71,7 @@ class FixedIpTest(test.TestCase): resp = req.get_response(fakes.wsgi_app()) self.assertEqual(resp.status_int, 202) - self.assertNotEqual(last_add_fixed_ip, (None, None)) + self.assertEqual(last_add_fixed_ip, ('test_inst', 'test_net')) def test_add_fixed_ip_no_network(self): global last_add_fixed_ip @@ -78,6 +88,9 @@ class FixedIpTest(test.TestCase): self.assertEqual(last_add_fixed_ip, (None, None)) def test_remove_fixed_ip(self): + global last_remove_fixed_ip + last_remove_fixed_ip = (None, None) + body = dict(removeFixedIp=dict(address='10.10.10.1')) req = webob.Request.blank('/v1.1/servers/test_inst/action') req.method = 'POST' @@ -85,4 +98,20 @@ class FixedIpTest(test.TestCase): req.headers['content-type'] = 'application/json' resp = req.get_response(fakes.wsgi_app()) - self.assertEqual(resp.status_int, 501) + # TODO(Vek): Fails until remove_fixed_ip() added + self.assertEqual(resp.status_int, 202) + self.assertEqual(last_remove_fixed_ip, ('test_inst', '10.10.10.1')) + + def test_remove_fixed_ip_no_address(self): + global last_remove_fixed_ip + last_remove_fixed_ip = (None, None) + + body = dict(removeFixedIp=dict()) + req = webob.Request.blank('/v1.1/servers/test_inst/action') + req.method = 'POST' + req.body = json.dumps(body) + req.headers['content-type'] = 'application/json' + + resp = req.get_response(fakes.wsgi_app()) + self.assertEqual(resp.status_int, 422) + self.assertEqual(last_remove_fixed_ip, (None, None)) -- cgit From c98f37c00d802abf2ac85cb6c800f39e1b067d72 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Fri, 8 Jul 2011 14:10:32 -0500 Subject: Add docstrings for multinic extension --- nova/api/openstack/contrib/multinic.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/nova/api/openstack/contrib/multinic.py b/nova/api/openstack/contrib/multinic.py index e202bd51c..841061721 100644 --- a/nova/api/openstack/contrib/multinic.py +++ b/nova/api/openstack/contrib/multinic.py @@ -29,26 +29,50 @@ LOG = logging.getLogger("nova.api.multinic") # Note: The class name is as it has to be for this to be loaded as an # extension--only first character capitalized. class Multinic(extensions.ExtensionDescriptor): + """The multinic extension. + + Exposes addFixedIp and removeFixedIp actions on servers. + + """ + def __init__(self, *args, **kwargs): + """Initialize the extension. + + Gets a compute.API object so we can call the back-end + add_fixed_ip() and remove_fixed_ip() methods. + """ + super(Multinic, self).__init__(*args, **kwargs) self.compute_api = compute.API() def get_name(self): + """Return the extension name, as required by contract.""" + return "Multinic" def get_alias(self): + """Return the extension alias, as required by contract.""" + return "NMN" def get_description(self): + """Return the extension description, as required by contract.""" + return "Multiple network support" def get_namespace(self): + """Return the namespace, as required by contract.""" + return "http://docs.openstack.org/ext/multinic/api/v1.1" def get_updated(self): + """Return the last updated timestamp, as required by contract.""" + return "2011-06-09T00:00:00+00:00" def get_actions(self): + """Return the actions the extension adds, as required by contract.""" + actions = [] # Add the add_fixed_ip action @@ -65,6 +89,7 @@ class Multinic(extensions.ExtensionDescriptor): def _add_fixed_ip(self, input_dict, req, id): """Adds an IP on a given network to an instance.""" + try: # Validate the input entity if 'networkId' not in input_dict['addFixedIp']: @@ -82,6 +107,7 @@ class Multinic(extensions.ExtensionDescriptor): def _remove_fixed_ip(self, input_dict, req, id): """Removes an IP from an instance.""" + try: # Validate the input entity if 'address' not in input_dict['removeFixedIp']: -- cgit From d326033f5ed4e8e24efddd4acf080ebfeb0caf2c Mon Sep 17 00:00:00 2001 From: Devendra Modium Date: Mon, 11 Jul 2011 10:48:32 -0400 Subject: Missing Author updated --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index 052f2d964..8d117c489 100644 --- a/Authors +++ b/Authors @@ -44,6 +44,7 @@ John Dewey John Tran Jonathan Bryce Jordan Rinke +Joseph Suh Josh Durgin Josh Kearney Josh Kleinpeter -- cgit From d2c628c0ecbae048fabaf60eff2afd4b8a9cd918 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Mon, 11 Jul 2011 10:18:17 -0500 Subject: Comment out these two asserts; Sandy will uncomment in his merge-prop --- nova/tests/api/openstack/contrib/test_multinic_xs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/tests/api/openstack/contrib/test_multinic_xs.py b/nova/tests/api/openstack/contrib/test_multinic_xs.py index b9f4456bb..484cd1c17 100644 --- a/nova/tests/api/openstack/contrib/test_multinic_xs.py +++ b/nova/tests/api/openstack/contrib/test_multinic_xs.py @@ -99,8 +99,8 @@ class FixedIpTest(test.TestCase): resp = req.get_response(fakes.wsgi_app()) # TODO(Vek): Fails until remove_fixed_ip() added - self.assertEqual(resp.status_int, 202) - self.assertEqual(last_remove_fixed_ip, ('test_inst', '10.10.10.1')) + # self.assertEqual(resp.status_int, 202) + # self.assertEqual(last_remove_fixed_ip, ('test_inst', '10.10.10.1')) def test_remove_fixed_ip_no_address(self): global last_remove_fixed_ip -- cgit