summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/nova-manage5
-rw-r--r--doc/api_samples/os-fixed-ips/fixedip-post-req.json3
-rw-r--r--doc/api_samples/os-fixed-ips/fixedip-post-req.xml2
-rw-r--r--doc/api_samples/os-fixed-ips/fixedips-get-resp.json8
-rw-r--r--doc/api_samples/os-fixed-ips/fixedips-get-resp.xml7
-rw-r--r--etc/nova/nova.conf.sample9
-rw-r--r--etc/nova/policy.json6
-rw-r--r--nova/db/sqlalchemy/api.py42
-rw-r--r--nova/loadables.py2
-rw-r--r--nova/locale/nova.pot144
-rw-r--r--nova/network/manager.py18
-rw-r--r--nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.json.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-fixed-ips/fixedip-post-req.xml.tpl2
-rw-r--r--nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.json.tpl8
-rw-r--r--nova/tests/integrated/api_samples/os-fixed-ips/fixedips-get-resp.xml.tpl7
-rw-r--r--nova/tests/integrated/test_api_samples.py86
-rw-r--r--nova/tests/network/test_manager.py8
-rw-r--r--nova/tests/test_db_api.py21
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()