1845
1970
fake_clear_pool.called = True
1846
1971
self.stubs.Set(self.conn._pool, "_clear_pool", fake_clear_pool)
1848
aggregate = self._aggregate_setup(aggr_state=aggregate_states.ACTIVE,
1849
metadata=self.fake_metadata)
1973
aggregate = self._aggregate_setup(metadata=self.fake_metadata)
1850
1974
self.conn._pool.remove_from_aggregate(self.context, aggregate, "host")
1851
1975
result = db.aggregate_get(self.context, aggregate.id)
1852
1976
self.assertTrue(fake_clear_pool.called)
1853
self.assertDictMatch({}, result.metadetails)
1854
self.assertEqual(aggregate_states.ACTIVE, result.operational_state)
1977
self.assertDictMatch({pool_states.POOL_FLAG: 'XenAPI',
1978
pool_states.KEY: pool_states.ACTIVE}, result.metadetails)
1856
1980
def test_remote_master_non_empty_pool(self):
1857
1981
"""Ensure AggregateError is raised if removing the master."""
1858
aggregate = self._aggregate_setup(aggr_state=aggregate_states.ACTIVE,
1859
hosts=['host', 'host2'],
1982
aggregate = self._aggregate_setup(hosts=['host', 'host2'],
1860
1983
metadata=self.fake_metadata)
1861
1985
self.assertRaises(exception.InvalidAggregateAction,
1862
1986
self.conn._pool.remove_from_aggregate,
1863
1987
self.context, aggregate, "host")
1865
1989
def _aggregate_setup(self, aggr_name='fake_aggregate',
1866
1990
aggr_zone='fake_zone',
1867
aggr_state=aggregate_states.CREATED,
1991
aggr_state=pool_states.CREATED,
1868
1992
hosts=['host'], metadata=None):
1869
1993
values = {"name": aggr_name,
1870
"availability_zone": aggr_zone,
1871
"operational_state": aggr_state, }
1994
"availability_zone": aggr_zone}
1872
1995
result = db.aggregate_create(self.context, values)
1996
pool_flag = {pool_states.POOL_FLAG: "XenAPI",
1997
pool_states.KEY: aggr_state}
1998
db.aggregate_metadata_add(self.context, result.id, pool_flag)
1873
2000
for host in hosts:
1874
2001
db.aggregate_host_add(self.context, result.id, host)
1876
2003
db.aggregate_metadata_add(self.context, result.id, metadata)
1877
2004
return db.aggregate_get(self.context, result.id)
2006
def test_add_host_to_aggregate_invalid_changing_status(self):
2007
"""Ensure InvalidAggregateAction is raised when adding host while
2008
aggregate is not ready."""
2009
aggregate = self._aggregate_setup(aggr_state=pool_states.CHANGING)
2010
self.assertRaises(exception.InvalidAggregateAction,
2011
self.conn.add_to_aggregate, self.context,
2014
def test_add_host_to_aggregate_invalid_dismissed_status(self):
2015
"""Ensure InvalidAggregateAction is raised when aggregate is
2017
aggregate = self._aggregate_setup(aggr_state=pool_states.DISMISSED)
2018
self.assertRaises(exception.InvalidAggregateAction,
2019
self.conn.add_to_aggregate, self.context,
2020
aggregate, 'fake_host')
2022
def test_add_host_to_aggregate_invalid_error_status(self):
2023
"""Ensure InvalidAggregateAction is raised when aggregate is
2025
aggregate = self._aggregate_setup(aggr_state=pool_states.ERROR)
2026
self.assertRaises(exception.InvalidAggregateAction,
2027
self.conn.add_to_aggregate, self.context,
2028
aggregate, 'fake_host')
2030
def test_remove_host_from_aggregate_error(self):
2031
"""Ensure we can remove a host from an aggregate even if in error."""
2032
values = _create_service_entries(self.context)
2033
fake_zone = values.keys()[0]
2034
aggr = self.api.create_aggregate(self.context,
2035
'fake_aggregate', fake_zone)
2036
# let's mock the fact that the aggregate is ready!
2037
metadata = {pool_states.POOL_FLAG: "XenAPI",
2038
pool_states.KEY: pool_states.ACTIVE}
2039
db.aggregate_metadata_add(self.context, aggr['id'], metadata)
2040
for host in values[fake_zone]:
2041
aggr = self.api.add_host_to_aggregate(self.context,
2043
# let's mock the fact that the aggregate is in error!
2044
status = {'operational_state': pool_states.ERROR}
2045
expected = self.api.remove_host_from_aggregate(self.context,
2047
values[fake_zone][0])
2048
self.assertEqual(len(aggr['hosts']) - 1, len(expected['hosts']))
2049
self.assertEqual(expected['metadata'][pool_states.KEY],
2052
def test_remove_host_from_aggregate_invalid_dismissed_status(self):
2053
"""Ensure InvalidAggregateAction is raised when aggregate is
2055
aggregate = self._aggregate_setup(aggr_state=pool_states.DISMISSED)
2056
self.assertRaises(exception.InvalidAggregateAction,
2057
self.conn.remove_from_aggregate, self.context,
2058
aggregate, 'fake_host')
2060
def test_remove_host_from_aggregate_invalid_changing_status(self):
2061
"""Ensure InvalidAggregateAction is raised when aggregate is
2063
aggregate = self._aggregate_setup(aggr_state=pool_states.CHANGING)
2064
self.assertRaises(exception.InvalidAggregateAction,
2065
self.conn.remove_from_aggregate, self.context,
2066
aggregate, 'fake_host')
2068
def test_add_aggregate_host_raise_err(self):
2069
"""Ensure the undo operation works correctly on add."""
2070
def fake_driver_add_to_aggregate(context, aggregate, host):
2071
raise exception.AggregateError
2072
self.stubs.Set(self.compute.driver, "add_to_aggregate",
2073
fake_driver_add_to_aggregate)
2074
metadata = {pool_states.POOL_FLAG: "XenAPI",
2075
pool_states.KEY: pool_states.ACTIVE}
2076
db.aggregate_metadata_add(self.context, self.aggr.id, metadata)
2077
db.aggregate_host_add(self.context, self.aggr.id, 'fake_host')
2079
self.assertRaises(exception.AggregateError,
2080
self.compute.add_aggregate_host,
2081
self.context, self.aggr.id, "fake_host")
2082
excepted = db.aggregate_get(self.context, self.aggr.id)
2083
self.assertEqual(excepted.metadetails[pool_states.KEY],
2085
self.assertEqual(excepted.hosts, [])
2088
class VmUtilsTestCase(test.TestCase):
2089
"""Unit tests for xenapi utils."""
2091
def test_upload_image(self):
2092
"""Ensure image properties include instance system metadata
2093
as well as few local settings."""
2095
def fake_instance_system_metadata_get(context, uuid):
2096
return dict(image_a=1, image_b=2, image_c='c', d='d')
2098
def fake_get_sr_path(session):
2101
class FakeInstance(dict):
2103
super(FakeInstance, self).__init__({
2104
'auto_disk_config': 'auto disk config',
2105
'os_type': 'os type'})
2107
def __missing__(self, item):
2110
class FakeSession(object):
2111
def call_plugin(session_self, service, command, kwargs):
2112
self.kwargs = kwargs
2114
def fake_dumps(thing):
2117
self.stubs.Set(db, "instance_system_metadata_get",
2118
fake_instance_system_metadata_get)
2119
self.stubs.Set(vm_utils, "get_sr_path", fake_get_sr_path)
2120
self.stubs.Set(pickle, "dumps", fake_dumps)
2122
ctx = context.get_admin_context()
2124
instance = FakeInstance()
2125
session = FakeSession()
2126
vm_utils.upload_image(ctx, session, instance, "vmi uuids", "image id")
2128
actual = self.kwargs['params']['properties']
2129
expected = dict(a=1, b=2, c='c', d='d',
2130
auto_disk_config='auto disk config',
2132
self.assertEquals(expected, actual)
2135
class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
2136
"""Unit tests for live_migration."""
2138
super(XenAPILiveMigrateTestCase, self).setUp()
2139
self.flags(xenapi_connection_url='test_url',
2140
xenapi_connection_password='test_pass',
2141
firewall_driver='nova.virt.xenapi.firewall.'
2142
'Dom0IptablesFirewallDriver',
2144
db_fakes.stub_out_db_instance_api(self.stubs)
2145
self.context = context.get_admin_context()
2146
xenapi_fake.create_local_pifs()
2148
def test_live_migration_calls_vmops(self):
2149
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2150
self.conn = xenapi_conn.XenAPIDriver(False)
2152
def fake_live_migrate(context, instance_ref, dest, post_method,
2153
recover_method, block_migration, migrate_data):
2154
fake_live_migrate.called = True
2156
self.stubs.Set(self.conn._vmops, "live_migrate", fake_live_migrate)
2158
self.conn.live_migration(None, None, None, None, None)
2159
self.assertTrue(fake_live_migrate.called)
2161
def test_pre_live_migration(self):
2162
# ensure method is present
2163
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2164
self.conn = xenapi_conn.XenAPIDriver(False)
2165
self.conn.pre_live_migration(None, None, None, None)
2167
def test_post_live_migration_at_destination(self):
2168
# ensure method is present
2169
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2170
self.conn = xenapi_conn.XenAPIDriver(False)
2171
self.conn.post_live_migration_at_destination(None, None, None, None)
2173
def test_check_can_live_migrate_destination_with_block_migration(self):
2174
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2175
self.conn = xenapi_conn.XenAPIDriver(False)
2176
expected = {'block_migration': True,
2177
'migrate_data': {'xenops': '',
2183
fake_data = self.conn.check_can_live_migrate_destination(self.context,
2184
{'host': 'host'}, True, False)
2185
self.assertEqual(expected.keys(), fake_data.keys())
2186
self.assertEqual(expected['migrate_data'].keys(),
2187
fake_data['migrate_data'].keys())
2189
def test_check_can_live_migrate_destination_block_migration_fails(self):
2190
stubs.stubout_session(self.stubs,
2191
stubs.FakeSessionForFailedMigrateTests)
2192
self.conn = xenapi_conn.XenAPIDriver(False)
2193
self.assertRaises(exception.MigrationError,
2194
self.conn.check_can_live_migrate_destination,
2195
self.context, {'host': 'host'}, True, False)
2197
def test_check_can_live_migrate_source_with_block_migrate(self):
2198
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2199
self.conn = xenapi_conn.XenAPIDriver(False)
2201
def fake_get_vm_opaque_ref(instance):
2204
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
2205
fake_get_vm_opaque_ref)
2206
dest_check_data = {'block_migration': True,
2208
self.assertNotRaises(None,
2209
self.conn.check_can_live_migrate_source,
2214
def test_check_can_live_migrate_source_with_block_migrate_fails(self):
2215
def fake_get_vm_opaque_ref(instance):
2217
stubs.stubout_session(self.stubs,
2218
stubs.FakeSessionForFailedMigrateTests)
2219
self.conn = xenapi_conn.XenAPIDriver(False)
2220
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
2221
fake_get_vm_opaque_ref)
2223
dest_check_data = {'block_migration': True,
2225
self.assertRaises(exception.MigrationError,
2226
self.conn.check_can_live_migrate_source,
2231
def test_check_can_live_migrate_works(self):
2232
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2233
self.conn = xenapi_conn.XenAPIDriver(False)
2235
class fake_aggregate:
2237
self.metadetails = {"host": "test_host_uuid"}
2239
def fake_aggregate_get_by_host(context, host, key=None):
2240
self.assertEqual(FLAGS.host, host)
2241
return [fake_aggregate()]
2243
self.stubs.Set(db, "aggregate_get_by_host",
2244
fake_aggregate_get_by_host)
2245
self.conn.check_can_live_migrate_destination(self.context,
2246
{'host': 'host'}, False, False)
2248
def test_check_can_live_migrate_fails(self):
2249
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2250
self.conn = xenapi_conn.XenAPIDriver(False)
2252
class fake_aggregate:
2254
self.metadetails = {"dest_other": "test_host_uuid"}
2256
def fake_aggregate_get_by_host(context, host, key=None):
2257
self.assertEqual(FLAGS.host, host)
2258
return [fake_aggregate()]
2260
self.stubs.Set(db, "aggregate_get_by_host",
2261
fake_aggregate_get_by_host)
2262
self.assertRaises(exception.MigrationError,
2263
self.conn.check_can_live_migrate_destination,
2264
self.context, {'host': 'host'}, None, None)
2266
def test_live_migration(self):
2267
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2268
self.conn = xenapi_conn.XenAPIDriver(False)
2270
def fake_get_vm_opaque_ref(instance):
2272
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
2273
fake_get_vm_opaque_ref)
2275
def fake_get_host_opaque_ref(context, destination_hostname):
2277
self.stubs.Set(self.conn._vmops, "_get_host_opaque_ref",
2278
fake_get_host_opaque_ref)
2280
def post_method(context, instance, destination_hostname,
2282
post_method.called = True
2284
self.conn.live_migration(self.conn, None, None, post_method, None)
2286
self.assertTrue(post_method.called, "post_method.called")
2288
def test_live_migration_on_failure(self):
2289
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2290
self.conn = xenapi_conn.XenAPIDriver(False)
2292
def fake_get_vm_opaque_ref(instance):
2294
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
2295
fake_get_vm_opaque_ref)
2297
def fake_get_host_opaque_ref(context, destination_hostname):
2299
self.stubs.Set(self.conn._vmops, "_get_host_opaque_ref",
2300
fake_get_host_opaque_ref)
2302
def fake_call_xenapi(*args):
2303
raise NotImplementedError()
2304
self.stubs.Set(self.conn._vmops._session, "call_xenapi",
2307
def recover_method(context, instance, destination_hostname,
2309
recover_method.called = True
2311
self.assertRaises(NotImplementedError, self.conn.live_migration,
2312
self.conn, None, None, None, recover_method)
2313
self.assertTrue(recover_method.called, "recover_method.called")
2315
def test_live_migration_with_block_migration(self):
2316
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2317
self.conn = xenapi_conn.XenAPIDriver(False)
2319
def fake_get_vm_opaque_ref(instance):
2321
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
2322
fake_get_vm_opaque_ref)
2324
def post_method(context, instance, destination_hostname,
2326
post_method.called = True
2328
# pass block_migration = True and migrate data
2329
migrate_data = {"test": "data"}
2330
self.conn.live_migration(self.conn, None, None, post_method, None,
2332
self.assertTrue(post_method.called, "post_method.called")
2334
def test_live_migration_with_block_migration_raises_invalid_param(self):
2335
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2336
self.conn = xenapi_conn.XenAPIDriver(False)
2338
def fake_get_vm_opaque_ref(instance):
2340
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
2341
fake_get_vm_opaque_ref)
2343
def recover_method(context, instance, destination_hostname,
2345
recover_method.called = True
2346
# pass block_migration = True and no migrate data
2347
self.assertRaises(exception.InvalidParameterValue,
2348
self.conn.live_migration, self.conn,
2349
None, None, None, recover_method, True, None)
2350
self.assertTrue(recover_method.called, "recover_method.called")
2352
def test_live_migration_with_block_migration_fails_migrate_send(self):
2353
stubs.stubout_session(self.stubs,
2354
stubs.FakeSessionForFailedMigrateTests)
2355
self.conn = xenapi_conn.XenAPIDriver(False)
2357
def fake_get_vm_opaque_ref(instance):
2359
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
2360
fake_get_vm_opaque_ref)
2362
def recover_method(context, instance, destination_hostname,
2364
recover_method.called = True
2365
# pass block_migration = True and migrate data
2366
migrate_data = {"test": "data"}
2367
self.assertRaises(exception.MigrationError,
2368
self.conn.live_migration, self.conn,
2369
None, None, None, recover_method, True, migrate_data)
2370
self.assertTrue(recover_method.called, "recover_method.called")
2373
class XenAPIInjectMetadataTestCase(stubs.XenAPITestBase):
2375
super(XenAPIInjectMetadataTestCase, self).setUp()
2376
self.flags(xenapi_connection_url='test_url',
2377
xenapi_connection_password='test_pass',
2378
firewall_driver='nova.virt.xenapi.firewall.'
2379
'Dom0IptablesFirewallDriver')
2380
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
2381
self.conn = xenapi_conn.XenAPIDriver(False)
2383
self.xenstore = dict(persist={}, ephem={})
2385
def fake_get_vm_opaque_ref(inst, instance):
2386
self.assertEqual(instance, 'instance')
2389
def fake_add_to_param_xenstore(inst, vm_ref, key, val):
2390
self.assertEqual(vm_ref, 'vm_ref')
2391
self.xenstore['persist'][key] = val
2393
def fake_remove_from_param_xenstore(inst, vm_ref, key):
2394
self.assertEqual(vm_ref, 'vm_ref')
2395
if key in self.xenstore['persist']:
2396
del self.xenstore['persist'][key]
2398
def fake_write_to_xenstore(inst, instance, path, value, vm_ref=None):
2399
self.assertEqual(instance, 'instance')
2400
self.assertEqual(vm_ref, 'vm_ref')
2401
self.xenstore['ephem'][path] = jsonutils.dumps(value)
2403
def fake_delete_from_xenstore(inst, instance, path, vm_ref=None):
2404
self.assertEqual(instance, 'instance')
2405
self.assertEqual(vm_ref, 'vm_ref')
2406
if path in self.xenstore['ephem']:
2407
del self.xenstore['ephem'][path]
2409
self.stubs.Set(vmops.VMOps, '_get_vm_opaque_ref',
2410
fake_get_vm_opaque_ref)
2411
self.stubs.Set(vmops.VMOps, '_add_to_param_xenstore',
2412
fake_add_to_param_xenstore)
2413
self.stubs.Set(vmops.VMOps, '_remove_from_param_xenstore',
2414
fake_remove_from_param_xenstore)
2415
self.stubs.Set(vmops.VMOps, '_write_to_xenstore',
2416
fake_write_to_xenstore)
2417
self.stubs.Set(vmops.VMOps, '_delete_from_xenstore',
2418
fake_delete_from_xenstore)
2420
def test_inject_instance_metadata(self):
2422
# Add some system_metadata to ensure it doesn't get added
2424
instance = dict(metadata=[{'key': 'a', 'value': 1},
2425
{'key': 'b', 'value': 2},
2426
{'key': 'c', 'value': 3},
2427
# Check xenstore key sanitizing
2428
{'key': 'hi.there', 'value': 4},
2429
{'key': 'hi!t.e/e', 'value': 5}],
2430
# Check xenstore key sanitizing
2431
system_metadata=[{'key': 'sys_a', 'value': 1},
2432
{'key': 'sys_b', 'value': 2},
2433
{'key': 'sys_c', 'value': 3}])
2434
self.conn._vmops.inject_instance_metadata(instance, 'vm_ref')
2436
self.assertEqual(self.xenstore, {
2438
'vm-data/user-metadata/a': '1',
2439
'vm-data/user-metadata/b': '2',
2440
'vm-data/user-metadata/c': '3',
2441
'vm-data/user-metadata/hi_there': '4',
2442
'vm-data/user-metadata/hi_t_e_e': '5',
2447
def test_change_instance_metadata_add(self):
2448
# Test XenStore key sanitizing here, too.
2449
diff = {'test.key': ['+', 4]}
2452
'vm-data/user-metadata/a': '1',
2453
'vm-data/user-metadata/b': '2',
2454
'vm-data/user-metadata/c': '3',
2457
'vm-data/user-metadata/a': '1',
2458
'vm-data/user-metadata/b': '2',
2459
'vm-data/user-metadata/c': '3',
2463
self.conn._vmops.change_instance_metadata('instance', diff)
2465
self.assertEqual(self.xenstore, {
2467
'vm-data/user-metadata/a': '1',
2468
'vm-data/user-metadata/b': '2',
2469
'vm-data/user-metadata/c': '3',
2470
'vm-data/user-metadata/test_key': '4',
2473
'vm-data/user-metadata/a': '1',
2474
'vm-data/user-metadata/b': '2',
2475
'vm-data/user-metadata/c': '3',
2476
'vm-data/user-metadata/test_key': '4',
2480
def test_change_instance_metadata_update(self):
2481
diff = dict(b=['+', 4])
2484
'vm-data/user-metadata/a': '1',
2485
'vm-data/user-metadata/b': '2',
2486
'vm-data/user-metadata/c': '3',
2489
'vm-data/user-metadata/a': '1',
2490
'vm-data/user-metadata/b': '2',
2491
'vm-data/user-metadata/c': '3',
2495
self.conn._vmops.change_instance_metadata('instance', diff)
2497
self.assertEqual(self.xenstore, {
2499
'vm-data/user-metadata/a': '1',
2500
'vm-data/user-metadata/b': '4',
2501
'vm-data/user-metadata/c': '3',
2504
'vm-data/user-metadata/a': '1',
2505
'vm-data/user-metadata/b': '4',
2506
'vm-data/user-metadata/c': '3',
2510
def test_change_instance_metadata_delete(self):
2511
diff = dict(b=['-'])
2514
'vm-data/user-metadata/a': '1',
2515
'vm-data/user-metadata/b': '2',
2516
'vm-data/user-metadata/c': '3',
2519
'vm-data/user-metadata/a': '1',
2520
'vm-data/user-metadata/b': '2',
2521
'vm-data/user-metadata/c': '3',
2525
self.conn._vmops.change_instance_metadata('instance', diff)
2527
self.assertEqual(self.xenstore, {
2529
'vm-data/user-metadata/a': '1',
2530
'vm-data/user-metadata/c': '3',
2533
'vm-data/user-metadata/a': '1',
2534
'vm-data/user-metadata/c': '3',