diff options
-rwxr-xr-x | bin/nova-manage | 5 | ||||
-rw-r--r-- | doc/api_samples/os-fixed-ips/fixedip-post-req.json | 3 | ||||
-rw-r--r-- | doc/api_samples/os-fixed-ips/fixedip-post-req.xml | 2 | ||||
-rw-r--r-- | doc/api_samples/os-fixed-ips/fixedips-get-resp.json | 8 | ||||
-rw-r--r-- | doc/api_samples/os-fixed-ips/fixedips-get-resp.xml | 7 | ||||
-rw-r--r-- | etc/nova/nova.conf.sample | 9 | ||||
-rw-r--r-- | etc/nova/policy.json | 6 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 42 | ||||
-rw-r--r-- | nova/loadables.py | 2 | ||||
-rw-r--r-- | nova/locale/nova.pot | 144 | ||||
-rw-r--r-- | nova/network/manager.py | 18 | ||||
-rw-r--r-- | nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.json.tpl | 3 | ||||
-rw-r--r-- | nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.xml.tpl | 2 | ||||
-rw-r--r-- | nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.json.tpl | 8 | ||||
-rw-r--r-- | nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.xml.tpl | 7 | ||||
-rw-r--r-- | nova/tests/integrated/test_api_samples.py | 86 | ||||
-rw-r--r-- | nova/tests/network/test_manager.py | 8 | ||||
-rw-r--r-- | nova/tests/test_db_api.py | 21 |
18 files changed, 278 insertions, 103 deletions
diff --git a/bin/nova-manage b/bin/nova-manage index 2ff96f50d..5d7e8d678 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -1129,8 +1129,11 @@ def main(): try: cliutils.validate_args(fn, *fn_args, **fn_kwargs) except cliutils.MissingArgs as e: + # NOTE(mikal): this isn't the most helpful error message ever. It is + # long, and tells you a lot of things you probably don't want to know + # if you just got a single arg wrong. print fn.__doc__ - parser.print_help() + CONF.print_help() print e sys.exit(1) try: diff --git a/doc/api_samples/os-fixed-ips/fixedip-post-req.json b/doc/api_samples/os-fixed-ips/fixedip-post-req.json new file mode 100644 index 000000000..cf8ba0e0b --- /dev/null +++ b/doc/api_samples/os-fixed-ips/fixedip-post-req.json @@ -0,0 +1,3 @@ +{ + "reserve": "None" +}
\ No newline at end of file diff --git a/doc/api_samples/os-fixed-ips/fixedip-post-req.xml b/doc/api_samples/os-fixed-ips/fixedip-post-req.xml new file mode 100644 index 000000000..e29b685be --- /dev/null +++ b/doc/api_samples/os-fixed-ips/fixedip-post-req.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<reserve>None</reserve>
\ No newline at end of file diff --git a/doc/api_samples/os-fixed-ips/fixedips-get-resp.json b/doc/api_samples/os-fixed-ips/fixedips-get-resp.json new file mode 100644 index 000000000..d63c91559 --- /dev/null +++ b/doc/api_samples/os-fixed-ips/fixedips-get-resp.json @@ -0,0 +1,8 @@ +{ + "fixed_ip": { + "address": "192.168.1.1", + "cidr": "192.168.1.0/24", + "host": "host", + "hostname": "openstack" + } +}
\ No newline at end of file diff --git a/doc/api_samples/os-fixed-ips/fixedips-get-resp.xml b/doc/api_samples/os-fixed-ips/fixedips-get-resp.xml new file mode 100644 index 000000000..a9676721f --- /dev/null +++ b/doc/api_samples/os-fixed-ips/fixedips-get-resp.xml @@ -0,0 +1,7 @@ +<?xml version='1.0' encoding='UTF-8'?> +<fixed_ip> + <cidr>192.168.1.0/24</cidr> + <hostname>openstack</hostname> + <host>host</host> + <address>192.168.1.1</address> +</fixed_ip>
\ No newline at end of file diff --git a/etc/nova/nova.conf.sample b/etc/nova/nova.conf.sample index 2fdd612b4..c3c10239c 100644 --- a/etc/nova/nova.conf.sample +++ b/etc/nova/nova.conf.sample @@ -920,6 +920,15 @@ # l3_lib=nova.network.l3.LinuxNetL3 #### (StrOpt) Indicates underlying L3 management library +# update_dns_entries=false +#### (BoolOpt) If True, when a DNS entry must be updated, it sends a fanout +#### cast to all network hosts to update their DNS entries in multi +#### host mode + +# dns_update_periodic_interval=-1 +#### (IntOpt) Number of periodic scheduler ticks to wait between runs of +#### updates to DNS entries + ######## defined in nova.network.quantumv2.api ######## diff --git a/etc/nova/policy.json b/etc/nova/policy.json index f1e16d834..d7596deab 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -46,7 +46,11 @@ "compute_extension:flavor_rxtx": "", "compute_extension:flavor_swap": "", "compute_extension:flavorextradata": "", - "compute_extension:flavorextraspecs": "", + "compute_extension:flavorextraspecs:index": "", + "compute_extension:flavorextraspecs:show": "", + "compute_extension:flavorextraspecs:create": "rule:admin_api", + "compute_extension:flavorextraspecs:update": "rule:admin_api", + "compute_extension:flavorextraspecs:delete": "rule:admin_api", "compute_extension:flavormanage": "rule:admin_api", "compute_extension:floating_ip_dns": "", "compute_extension:floating_ip_pools": "", diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 9cc3b64a1..8e454ec78 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1768,38 +1768,40 @@ def instance_get_all_hung_in_rebooting(context, reboot_window): @require_context -def instance_test_and_set(context, instance_uuid, attr, ok_states, - new_state, session=None): +def instance_test_and_set(context, instance_uuid, attr, ok_states, new_state): """Atomically check if an instance is in a valid state, and if it is, set the instance into a new state. """ - if not session: - session = get_session() + if not uuidutils.is_uuid_like(instance_uuid): + raise exception.InvalidUUID(instance_uuid) + session = get_session() with session.begin(): query = model_query(context, models.Instance, session=session, - project_only=True) - - if uuidutils.is_uuid_like(instance_uuid): - query = query.filter_by(uuid=instance_uuid) + project_only=True).\ + filter_by(uuid=instance_uuid) + + attr_column = getattr(models.Instance, attr) + filter_op = None + # NOTE(boris-42): `SELECT IN` doesn't work with None values because + # they are incomparable. + if None in ok_states: + filter_op = or_(attr_column == None, + attr_column.in_(filter(lambda x: x is not None, + ok_states))) else: - raise exception.InvalidUUID(instance_uuid) - - # NOTE(vish): if with_lockmode isn't supported, as in sqlite, - # then this has concurrency issues - instance = query.with_lockmode('update').first() + filter_op = attr_column.in_(ok_states) - state = instance[attr] - if state not in ok_states: + count = query.filter(filter_op).\ + update({attr: new_state}, synchronize_session=False) + if count == 0: + instance_ref = query.first() raise exception.InstanceInvalidState( attr=attr, - instance_uuid=instance['uuid'], - state=state, + instance_uuid=instance_ref['uuid'], + state=instance_ref[attr], method='instance_test_and_set') - instance[attr] = new_state - instance.save(session=session) - @require_context def instance_update(context, instance_uuid, values): diff --git a/nova/loadables.py b/nova/loadables.py index 0c930267e..964845184 100644 --- a/nova/loadables.py +++ b/nova/loadables.py @@ -49,7 +49,7 @@ from nova.openstack.common import importutils class BaseLoader(object): def __init__(self, loadable_cls_type): mod = sys.modules[self.__class__.__module__] - self.path = mod.__path__[0] + self.path = os.path.abspath(mod.__path__[0]) self.package = mod.__package__ self.loadable_cls_type = loadable_cls_type diff --git a/nova/locale/nova.pot b/nova/locale/nova.pot index af5c0fc32..7ed43bf34 100644 --- a/nova/locale/nova.pot +++ b/nova/locale/nova.pot @@ -1,14 +1,14 @@ # Translations template for nova. -# Copyright (C) 2012 ORGANIZATION +# Copyright (C) 2013 ORGANIZATION # This file is distributed under the same license as the nova project. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2013. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: nova 2013.1\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2012-12-27 00:01+0000\n" +"POT-Creation-Date: 2013-01-01 00:02+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -5178,17 +5178,17 @@ msgstr "" msgid "unexpected role header" msgstr "" -#: nova/tests/api/openstack/compute/test_servers.py:3052 +#: nova/tests/api/openstack/compute/test_servers.py:3059 msgid "" "Quota exceeded for instances: Requested 1, but already used 10 of 10 " "instances" msgstr "" -#: nova/tests/api/openstack/compute/test_servers.py:3057 +#: nova/tests/api/openstack/compute/test_servers.py:3064 msgid "Quota exceeded for ram: Requested 4096, but already used 8192 of 10240 ram" msgstr "" -#: nova/tests/api/openstack/compute/test_servers.py:3062 +#: nova/tests/api/openstack/compute/test_servers.py:3069 msgid "Quota exceeded for cores: Requested 2, but already used 9 of 10 cores" msgstr "" @@ -5310,12 +5310,12 @@ msgid "" "arguments \"%(params)s\"" msgstr "" -#: nova/tests/integrated/test_api_samples.py:151 +#: nova/tests/integrated/test_api_samples.py:153 #, python-format msgid "Result: %(result)s is not a dict." msgstr "" -#: nova/tests/integrated/test_api_samples.py:155 +#: nova/tests/integrated/test_api_samples.py:157 #, python-format msgid "" "Key mismatch:\n" @@ -5323,25 +5323,25 @@ msgid "" "%(res_keys)s" msgstr "" -#: nova/tests/integrated/test_api_samples.py:163 +#: nova/tests/integrated/test_api_samples.py:165 #, python-format msgid "Result: %(result)s is not a list." msgstr "" -#: nova/tests/integrated/test_api_samples.py:166 +#: nova/tests/integrated/test_api_samples.py:168 #, python-format msgid "" "Length mismatch: %(result)s\n" "%(expected)s." msgstr "" -#: nova/tests/integrated/test_api_samples.py:177 +#: nova/tests/integrated/test_api_samples.py:179 #, python-format msgid "Result: %(res_obj)s not in %(expected)s." msgstr "" -#: nova/tests/integrated/test_api_samples.py:195 -#: nova/tests/integrated/test_api_samples.py:208 +#: nova/tests/integrated/test_api_samples.py:197 +#: nova/tests/integrated/test_api_samples.py:210 #, python-format msgid "" "Values do not match:\n" @@ -5601,31 +5601,31 @@ msgstr "" msgid "Inject metadata fs=%(fs)s metadata=%(metadata)s" msgstr "" -#: nova/virt/disk/api.py:376 +#: nova/virt/disk/api.py:379 #, python-format msgid "Inject key fs=%(fs)s key=%(key)s" msgstr "" -#: nova/virt/disk/api.py:404 +#: nova/virt/disk/api.py:407 #, python-format msgid "Inject key fs=%(fs)s net=%(net)s" msgstr "" -#: nova/virt/disk/api.py:430 +#: nova/virt/disk/api.py:433 #, python-format msgid "Inject admin password fs=%(fs)s admin_passwd=ha-ha-not-telling-you" msgstr "" -#: nova/virt/disk/api.py:475 +#: nova/virt/disk/api.py:478 msgid "Not implemented on Windows" msgstr "" -#: nova/virt/disk/api.py:504 +#: nova/virt/disk/api.py:507 #, python-format msgid "User %(username)s not found in password file." msgstr "" -#: nova/virt/disk/api.py:520 +#: nova/virt/disk/api.py:523 #, python-format msgid "User %(username)s not found in shadow file." msgstr "" @@ -5930,7 +5930,7 @@ msgstr "" msgid "The ISCSI initiator name can't be found. Choosing the default one" msgstr "" -#: nova/virt/hyperv/basevolumeutils.py:79 nova/virt/libvirt/driver.py:1494 +#: nova/virt/hyperv/basevolumeutils.py:79 nova/virt/libvirt/driver.py:1422 #: nova/virt/xenapi/vm_utils.py:504 #, python-format msgid "block_device_list %s" @@ -5979,7 +5979,7 @@ msgstr "" msgid "get_available_resource called" msgstr "" -#: nova/virt/hyperv/hostops.py:163 nova/virt/libvirt/driver.py:3179 +#: nova/virt/hyperv/hostops.py:163 nova/virt/libvirt/driver.py:3107 #: nova/virt/xenapi/host.py:149 msgid "Updating host stats" msgstr "" @@ -6163,16 +6163,16 @@ msgstr "" msgid "Invalid config_drive_format \"%s\"" msgstr "" -#: nova/virt/hyperv/vmops.py:183 nova/virt/libvirt/driver.py:1432 +#: nova/virt/hyperv/vmops.py:183 nova/virt/libvirt/driver.py:1360 msgid "Using config drive" msgstr "" -#: nova/virt/hyperv/vmops.py:194 nova/virt/libvirt/driver.py:1442 +#: nova/virt/hyperv/vmops.py:194 nova/virt/libvirt/driver.py:1370 #, python-format msgid "Creating config drive at %(path)s" msgstr "" -#: nova/virt/hyperv/vmops.py:201 nova/virt/libvirt/driver.py:1447 +#: nova/virt/hyperv/vmops.py:201 nova/virt/libvirt/driver.py:1375 #, python-format msgid "Creating config drive failed with error: %s" msgstr "" @@ -6599,178 +6599,170 @@ msgstr "" msgid "Failed to cleanup directory %(target)s: %(e)s" msgstr "" -#: nova/virt/libvirt/driver.py:747 +#: nova/virt/libvirt/driver.py:730 msgid "During detach_volume, instance disappeared." msgstr "" -#: nova/virt/libvirt/driver.py:757 -msgid "attaching LXC block device" -msgstr "" - -#: nova/virt/libvirt/driver.py:777 -msgid "detaching LXC block device" -msgstr "" - -#: nova/virt/libvirt/driver.py:909 +#: nova/virt/libvirt/driver.py:837 msgid "Instance soft rebooted successfully." msgstr "" -#: nova/virt/libvirt/driver.py:913 +#: nova/virt/libvirt/driver.py:841 msgid "Failed to soft reboot instance." msgstr "" -#: nova/virt/libvirt/driver.py:945 +#: nova/virt/libvirt/driver.py:873 msgid "Instance shutdown successfully." msgstr "" -#: nova/virt/libvirt/driver.py:981 +#: nova/virt/libvirt/driver.py:909 msgid "Instance rebooted successfully." msgstr "" -#: nova/virt/libvirt/driver.py:1109 +#: nova/virt/libvirt/driver.py:1037 msgid "Instance is running" msgstr "" -#: nova/virt/libvirt/driver.py:1116 nova/virt/powervm/operator.py:255 +#: nova/virt/libvirt/driver.py:1044 nova/virt/powervm/operator.py:255 msgid "Instance spawned successfully." msgstr "" -#: nova/virt/libvirt/driver.py:1132 +#: nova/virt/libvirt/driver.py:1060 #, python-format msgid "data: %(data)r, fpath: %(fpath)r" msgstr "" -#: nova/virt/libvirt/driver.py:1170 nova/virt/libvirt/driver.py:1196 +#: nova/virt/libvirt/driver.py:1098 nova/virt/libvirt/driver.py:1124 #, python-format msgid "Truncated console log returned, %d bytes ignored" msgstr "" -#: nova/virt/libvirt/driver.py:1185 +#: nova/virt/libvirt/driver.py:1113 msgid "Guest does not have a console available" msgstr "" -#: nova/virt/libvirt/driver.py:1234 +#: nova/virt/libvirt/driver.py:1162 #, python-format msgid "Path '%(path)s' supports direct I/O" msgstr "" -#: nova/virt/libvirt/driver.py:1238 +#: nova/virt/libvirt/driver.py:1166 #, python-format msgid "Path '%(path)s' does not support direct I/O: '%(ex)s'" msgstr "" -#: nova/virt/libvirt/driver.py:1242 nova/virt/libvirt/driver.py:1246 +#: nova/virt/libvirt/driver.py:1170 nova/virt/libvirt/driver.py:1174 #, python-format msgid "Error on '%(path)s' while checking direct I/O: '%(ex)s'" msgstr "" -#: nova/virt/libvirt/driver.py:1312 +#: nova/virt/libvirt/driver.py:1240 msgid "Creating image" msgstr "" -#: nova/virt/libvirt/driver.py:1462 +#: nova/virt/libvirt/driver.py:1390 #, python-format msgid "Injecting %(injection)s into image %(img_id)s" msgstr "" -#: nova/virt/libvirt/driver.py:1472 +#: nova/virt/libvirt/driver.py:1400 #, python-format msgid "Ignoring error injecting data into image %(img_id)s (%(e)s)" msgstr "" -#: nova/virt/libvirt/driver.py:1546 +#: nova/virt/libvirt/driver.py:1474 #, python-format msgid "" "Config requested an explicit CPU model, but the current libvirt " "hypervisor '%s' does not support selecting CPU models" msgstr "" -#: nova/virt/libvirt/driver.py:1552 +#: nova/virt/libvirt/driver.py:1480 msgid "Config requested a custom CPU model, but no model name was provided" msgstr "" -#: nova/virt/libvirt/driver.py:1556 +#: nova/virt/libvirt/driver.py:1484 msgid "A CPU model name should not be set when a host CPU model is requested" msgstr "" -#: nova/virt/libvirt/driver.py:1560 +#: nova/virt/libvirt/driver.py:1488 #, python-format msgid "CPU mode '%(mode)s' model '%(model)s' was chosen" msgstr "" -#: nova/virt/libvirt/driver.py:1576 +#: nova/virt/libvirt/driver.py:1504 msgid "" "Passthrough of the host CPU was requested but this libvirt version does " "not support this feature" msgstr "" -#: nova/virt/libvirt/driver.py:1893 +#: nova/virt/libvirt/driver.py:1821 msgid "Starting toXML method" msgstr "" -#: nova/virt/libvirt/driver.py:1897 +#: nova/virt/libvirt/driver.py:1825 msgid "Finished toXML method" msgstr "" -#: nova/virt/libvirt/driver.py:1914 +#: nova/virt/libvirt/driver.py:1842 #, python-format msgid "" "Error from libvirt while looking up %(instance_name)s: [Error Code " "%(error_code)s] %(ex)s" msgstr "" -#: nova/virt/libvirt/driver.py:2068 +#: nova/virt/libvirt/driver.py:1996 msgid "" "Cannot get the number of cpu, because this function is not implemented " "for this platform. " msgstr "" -#: nova/virt/libvirt/driver.py:2185 +#: nova/virt/libvirt/driver.py:2113 msgid "libvirt version is too old (does not support getVersion)" msgstr "" -#: nova/virt/libvirt/driver.py:2268 +#: nova/virt/libvirt/driver.py:2196 #, python-format msgid "Trying to get stats for the volume %s" msgstr "" -#: nova/virt/libvirt/driver.py:2292 +#: nova/virt/libvirt/driver.py:2220 #, python-format msgid "" "Getting block stats failed, device might have been detached. " "Code=%(errcode)s Error=%(e)s" msgstr "" -#: nova/virt/libvirt/driver.py:2296 +#: nova/virt/libvirt/driver.py:2224 #, python-format msgid "" "Could not find domain in libvirt for instance %s. Cannot get block stats " "for device" msgstr "" -#: nova/virt/libvirt/driver.py:2412 +#: nova/virt/libvirt/driver.py:2340 msgid "Block migration can not be used with shared storage." msgstr "" -#: nova/virt/libvirt/driver.py:2420 +#: nova/virt/libvirt/driver.py:2348 msgid "Live migration can not be used without shared storage." msgstr "" -#: nova/virt/libvirt/driver.py:2457 +#: nova/virt/libvirt/driver.py:2385 #, python-format msgid "" "Unable to migrate %(instance_uuid)s: Disk of instance is too " "large(available on destination host:%(available)s < need:%(necessary)s)" msgstr "" -#: nova/virt/libvirt/driver.py:2482 +#: nova/virt/libvirt/driver.py:2410 #, python-format msgid "" "Instance launched has CPU info:\n" "%s" msgstr "" -#: nova/virt/libvirt/driver.py:2494 +#: nova/virt/libvirt/driver.py:2422 #, python-format msgid "" "CPU doesn't have compatibility.\n" @@ -6780,51 +6772,51 @@ msgid "" "Refer to %(u)s" msgstr "" -#: nova/virt/libvirt/driver.py:2511 +#: nova/virt/libvirt/driver.py:2439 #, python-format msgid "" "Creating tmpfile %s to notify to other compute nodes that they should " "mount the same storage." msgstr "" -#: nova/virt/libvirt/driver.py:2559 +#: nova/virt/libvirt/driver.py:2487 #, python-format msgid "The firewall filter for %s does not exist" msgstr "" -#: nova/virt/libvirt/driver.py:2631 +#: nova/virt/libvirt/driver.py:2559 #, python-format msgid "Live Migration failure: %(e)s" msgstr "" -#: nova/virt/libvirt/driver.py:2721 +#: nova/virt/libvirt/driver.py:2649 #, python-format msgid "plug_vifs() failed %(cnt)d.Retry up to %(max_retry)d for %(hostname)s." msgstr "" -#: nova/virt/libvirt/driver.py:2836 +#: nova/virt/libvirt/driver.py:2764 #, python-format msgid "skipping %(path)s since it looks like volume" msgstr "" -#: nova/virt/libvirt/driver.py:2885 +#: nova/virt/libvirt/driver.py:2813 #, python-format msgid "Getting disk size of %(i_name)s: %(e)s" msgstr "" -#: nova/virt/libvirt/driver.py:2934 +#: nova/virt/libvirt/driver.py:2862 msgid "Starting migrate_disk_and_power_off" msgstr "" -#: nova/virt/libvirt/driver.py:2993 +#: nova/virt/libvirt/driver.py:2921 msgid "Instance running successfully." msgstr "" -#: nova/virt/libvirt/driver.py:3000 +#: nova/virt/libvirt/driver.py:2928 msgid "Starting finish_migration" msgstr "" -#: nova/virt/libvirt/driver.py:3051 +#: nova/virt/libvirt/driver.py:2979 msgid "Starting finish_revert_migration" msgstr "" diff --git a/nova/network/manager.py b/nova/network/manager.py index 97d4fa10d..0e8530d14 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -170,6 +170,10 @@ network_opts = [ help='If True, when a DNS entry must be updated, it sends a ' 'fanout cast to all network hosts to update their DNS ' 'entries in multi host mode'), + cfg.IntOpt("dns_update_periodic_interval", + default=-1, + help='Number of periodic scheduler ticks to wait between ' + 'runs of updates to DNS entries.'), cfg.StrOpt('dhcp_domain', default='novalocal', help='domain to use for building the hostnames'), @@ -543,6 +547,9 @@ class FloatingIP(object): # find previously associated instance fixed_ip = self.db.fixed_ip_get(context, floating_ip['fixed_ip_id']) + if fixed_ip['address'] == fixed_address: + # NOTE(vish): already associated to this address + return orig_instance_uuid = fixed_ip['instance_uuid'] self.disassociate_floating_ip(context, floating_address) @@ -1912,7 +1919,7 @@ class NetworkManager(manager.SchedulerDependentManager): @wrap_check_policy def get_network(self, context, network_uuid): network = self.db.network_get_by_uuid(context.elevated(), network_uuid) - return dict(network.iteritems()) + return jsonutils.to_primitive(network) @wrap_check_policy def get_all_networks(self, context): @@ -1943,6 +1950,15 @@ class NetworkManager(manager.SchedulerDependentManager): return self.db.virtual_interface_get_by_address(context, mac_address) + @manager.periodic_task( + ticks_between_runs=CONF.dns_update_periodic_interval) + def _periodic_update_dns(self, context): + """Update local DNS entries of all networks on this host""" + networks = self.db.network_get_all_by_host(context, self.host) + for network in networks: + dev = self.driver.get_dev(network) + self.driver.update_dns(context, dev, network) + def update_dns(self, context, network_ids): """Called when fixed IP is allocated or deallocated""" if CONF.fake_network: diff --git a/nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.json.tpl b/nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.json.tpl new file mode 100644 index 000000000..85ae4890a --- /dev/null +++ b/nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.json.tpl @@ -0,0 +1,3 @@ +{ + "reserve": "%(reserve)s" +} diff --git a/nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.xml.tpl b/nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.xml.tpl new file mode 100644 index 000000000..3896b24eb --- /dev/null +++ b/nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.xml.tpl @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<reserve>%(reserve)s</reserve> diff --git a/nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.json.tpl new file mode 100644 index 000000000..a3d11475b --- /dev/null +++ b/nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.json.tpl @@ -0,0 +1,8 @@ +{ + "fixed_ip": { + "cidr": "%(cidr)s", + "hostname": "%(hostname)s", + "host": "%(host)s", + "address": "%(address)s" + } +} diff --git a/nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.xml.tpl new file mode 100644 index 000000000..3e9598f34 --- /dev/null +++ b/nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.xml.tpl @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<fixed_ip> + <cidr>%(cidr)s</cidr> + <hostname>%(hostname)s</hostname> + <host>%(host)s</host> + <address>%(address)s</address> +</fixed_ip> diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index a8099a6bb..cbcbb3375 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -32,6 +32,7 @@ from nova.compute import api from nova import context from nova import db from nova.db.sqlalchemy import models +from nova import exception from nova.network import api from nova.network.manager import NetworkManager from nova.openstack.common import cfg @@ -356,7 +357,6 @@ class ApiSamplesTrap(ApiSampleTestBase): do_not_approve_additions.append('OS-FLV-DISABLED') do_not_approve_additions.append('os-config-drive') do_not_approve_additions.append('os-create-server-ext') - do_not_approve_additions.append('os-fixed-ips') do_not_approve_additions.append('os-flavor-access') do_not_approve_additions.append('os-flavor-extra-specs') do_not_approve_additions.append('os-flavor-rxtx') @@ -1559,6 +1559,90 @@ class AgentsXmlTest(AgentsJsonTest): ctype = "xml" +class FixedIpJsonTest(ApiSampleTestBase): + extension_name = "nova.api.openstack.compute.contrib.fixed_ips.Fixed_ips" + + def _get_flags(self): + f = super(FixedIpJsonTest, self)._get_flags() + f['osapi_compute_extension'] = CONF.osapi_compute_extension[:] + return f + + def setUp(self): + super(FixedIpJsonTest, self).setUp() + + fake_fixed_ips = [{'id': 1, + 'address': '192.168.1.1', + 'network_id': 1, + 'virtual_interface_id': 1, + 'instance_uuid': '1', + 'allocated': False, + 'leased': False, + 'reserved': False, + 'host': None}, + {'id': 2, + 'address': '192.168.1.2', + 'network_id': 1, + 'virtual_interface_id': 2, + 'instance_uuid': '2', + 'allocated': False, + 'leased': False, + 'reserved': False, + 'host': None}, + ] + + def fake_fixed_ip_get_by_address(context, address): + for fixed_ip in fake_fixed_ips: + if fixed_ip['address'] == address: + return fixed_ip + raise exception.FixedIpNotFoundForAddress(address=address) + + def fake_fixed_ip_get_by_address_detailed(context, address): + network = {'id': 1, + 'cidr': "192.168.1.0/24"} + host = {'host': "host", + 'hostname': 'openstack'} + for fixed_ip in fake_fixed_ips: + if fixed_ip['address'] == address: + return (fixed_ip, network, host) + raise exception.FixedIpNotFoundForAddress(address=address) + + def fake_fixed_ip_update(context, address, values): + fixed_ip = fake_fixed_ip_get_by_address(context, address) + if fixed_ip is None: + raise exception.FixedIpNotFoundForAddress(address=address) + else: + for key in values: + fixed_ip[key] = values[key] + + self.stubs.Set(db, "fixed_ip_get_by_address", + fake_fixed_ip_get_by_address) + self.stubs.Set(db, "fixed_ip_get_by_address_detailed", + fake_fixed_ip_get_by_address_detailed) + self.stubs.Set(db, "fixed_ip_update", fake_fixed_ip_update) + + def test_fixed_ip_reserve(self): + """Reserve a Fixed IP""" + project = {'reserve': None} + response = self._do_post('os-fixed-ips/192.168.1.1/action', + 'fixedip-post-req', + project) + self.assertEqual(response.status, 202) + + def test_get_fixed_ip(self): + """Return data about the given fixed ip.""" + response = self._do_get('os-fixed-ips/192.168.1.1') + self.assertEqual(response.status, 200) + project = {'cidr': '192.168.1.0/24', + 'hostname': 'openstack', + 'host': 'host', + 'address': '192.168.1.1'} + return self._verify_response('fixedips-get-resp', project, response) + + +class FixedIpXmlTest(FixedIpJsonTest): + ctype = "xml" + + class AggregatesSampleJsonTest(ServersSampleBase): extension_name = "nova.api.openstack.compute.contrib" + \ ".aggregates.Aggregates" diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py index 0e441eef8..c2aa1dbbb 100644 --- a/nova/tests/network/test_manager.py +++ b/nova/tests/network/test_manager.py @@ -740,15 +740,19 @@ class VlanNetworkTestCase(test.TestCase): self.stubs.Set(self.network, 'disassociate_floating_ip', fake9) def fake_fixed_ip_get(context, fixed_ip_id): - return {'instance_uuid': 'fake_uuid'} + return {'address': 'old', 'instance_uuid': 'fake_uuid'} self.stubs.Set(self.network.db, 'fixed_ip_get', fake_fixed_ip_get) + # doesn't raise because we exit early if the address is the same + self.network.associate_floating_ip(ctxt, mox.IgnoreArg(), 'old') + + # raises because we call disassociate which is mocked self.assertRaises(test.TestingException, self.network.associate_floating_ip, ctxt, mox.IgnoreArg(), - mox.IgnoreArg()) + 'new') self.stubs.Set(self.network.db, 'floating_ip_get_by_address', fake3) diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index 29bce8bf5..ea6e9aea5 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -284,6 +284,27 @@ class DbApiTestCase(test.TestCase): self.assertRaises(exception.DuplicateVlan, db.network_create_safe, ctxt, values2) + def test_instance_test_and_set(self): + ctxt = context.get_admin_context() + states = [ + (None, [None, 'some'], 'building'), + (None, [None], 'building'), + ('building', ['building'], 'ready'), + ('building', [None, 'building'], 'ready')] + for st in states: + inst = db.instance_create(ctxt, {'vm_state': st[0]}) + uuid = inst['uuid'] + db.instance_test_and_set(ctxt, uuid, 'vm_state', st[1], st[2]) + inst = db.instance_get_by_uuid(ctxt, uuid) + self.assertEqual(inst["vm_state"], st[2]) + + def test_instance_test_and_set_exception(self): + ctxt = context.get_admin_context() + inst = db.instance_create(ctxt, {'vm_state': 'building'}) + self.assertRaises(exception.InstanceInvalidState, + db.instance_test_and_set, ctxt, + inst['uuid'], 'vm_state', [None, 'disable'], 'run') + def test_instance_update_with_instance_uuid(self): """ test instance_update() works when an instance UUID is passed """ ctxt = context.get_admin_context() |