From 2925ca3ac3010b9a65276ad2cfc8118679827da3 Mon Sep 17 00:00:00 2001 From: masumotok Date: Tue, 7 Dec 2010 19:25:43 +0900 Subject: rev439ベースにライブマイグレーションの機能をマージ このバージョンはEBSなし、CPUフラグのチェックなし MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/api/ec2/cloud.py | 13 +++- nova/compute/api.py | 3 + nova/compute/manager.py | 138 +++++++++++++++++++++++++++++++++++++++++++ nova/db/api.py | 69 ++++++++++++++++++++++ nova/db/sqlalchemy/api.py | 131 ++++++++++++++++++++++++++++++++++++++++ nova/db/sqlalchemy/models.py | 24 +++++++- nova/network/manager.py | 7 +++ nova/scheduler/manager.py | 107 +++++++++++++++++++++++++++++++++ nova/service.py | 22 +++++++ nova/utils.py | 13 +++- nova/virt/libvirt_conn.py | 101 +++++++++++++++++++++++++++++++ 11 files changed, 622 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index e50906ae1..ebf5bcf0b 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -678,13 +678,22 @@ class CloudController(object): ec2_id = None if (floating_ip_ref['fixed_ip'] and floating_ip_ref['fixed_ip']['instance']): - internal_id = floating_ip_ref['fixed_ip']['instance']['ec2_id'] + # modified by masumotok + internal_id = \ + floating_ip_ref['fixed_ip']['instance']['internal_id'] ec2_id = internal_id_to_ec2_id(internal_id) address_rv = {'public_ip': address, 'instance_id': ec2_id} if context.user.is_admin(): + # modified by masumotok- b/c proj_id is never inserted + #details = "%s (%s)" % (address_rv['instance_id'], + # floating_ip_ref['project_id']) + if None != address_rv['instance_id']: + status = 'reserved' + else: + status = None details = "%s (%s)" % (address_rv['instance_id'], - floating_ip_ref['project_id']) + status) address_rv['instance_id'] = details addresses.append(address_rv) return {'addressesSet': addresses} diff --git a/nova/compute/api.py b/nova/compute/api.py index 929342a1e..03922c272 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -84,6 +84,7 @@ class ComputeAPI(base.Base): if not type(security_group) is list: security_group = [security_group] + print '<<<<<<<<<<<<<<<<<<<<<<<<<<1' security_groups = [] self.ensure_default_security_group(context) for security_group_name in security_group: @@ -92,6 +93,7 @@ class ComputeAPI(base.Base): security_group_name) security_groups.append(group['id']) + print '<<<<<<<<<<<<<<<<<<<<<<<<<<2' if key_data is None and key_name: key_pair = db.key_pair_get(context, context.user_id, key_name) key_data = key_pair['public_key'] @@ -115,6 +117,7 @@ class ComputeAPI(base.Base): 'key_name': key_name, 'key_data': key_data} + print '<<<<<<<<<<<<<<<<<<<<<<<<<<3' elevated = context.elevated() instances = [] logging.debug("Going to run %s instances...", num_instances) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index dd8d41129..4c42153b6 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -36,6 +36,13 @@ terminating it. import datetime import logging +# added by masumotok +import sys +# added by masumotok +import traceback +# added by masumotok +import os + from twisted.internet import defer @@ -44,12 +51,19 @@ from nova import flags from nova import manager from nova import utils from nova.compute import power_state +# added by masumotok +from nova import rpc +# added by masumotok +from nova import db FLAGS = flags.FLAGS flags.DEFINE_string('instances_path', '$state_path/instances', 'where instances are stored on disk') flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection', 'Driver to use for controlling virtualization') +# created by masumotok +flags.DEFINE_string('live_migration_timeout', 30, + 'Timeout value for pre_live_migration is completed.') class ComputeManager(manager.Manager): @@ -251,3 +265,127 @@ class ComputeManager(manager.Manager): yield self.volume_manager.remove_compute_volume(context, volume_id) self.db.volume_detached(context, volume_id) defer.returnValue(True) + + # created by masumotok + def get_cpu_number(self): + """Get the number of physical computer cpu core .""" + return open('/proc/cpuinfo').read().count('processor') + + # created by masumotok + def get_mem_size(self): + """Get the memory size of physical computer .""" + meminfo = open('/proc/meminfo').read().split() + idx = meminfo.index('MemTotal:') + # transforming kb to mb. + return int(meminfo[idx + 1]) / 1024 + + # created by masumotok + def get_hdd_size(self): + """Get the hdd size of physical computer .""" + hddinfo = os.statvfs(FLAGS.instances_path) + return hddinfo.f_bsize * hddinfo.f_blocks / 1024 / 1024 / 1024 + + # created by masumotok + def pre_live_migration(self, context, instance_id, dest): + """Any preparation for live migration at dst host.""" + + # 1. getting volume info ( shlf/slot number ) + instance_ref = db.instance_get(context, instance_id) + ec2_id = instance_ref['hostname'] + + volumes = [] + try: + volumes = db.volume_get_by_ec2_id(context, ec2_id) + except exception.NotFound: + logging.debug('%s has no volume.', ec2_id) + + shelf_slots = {} + for vol in volumes: + shelf, slot = db.volume_get_shelf_and_blade(context, vol['id']) + shelf_slots[vol.id] = (shelf, slot) + + # 2. getting fixed ips + fixed_ip = db.instance_get_fixed_address(context, instance_id) + if None == fixed_ip: + logging.error('Not found fixedip for %s\n%s', + ec2_id, + ''.join(traceback.format_tb(sys.exc_info()[2]))) + return + + # 3. getting network refs + network_ref = db.fixed_ip_get_network(context, fixed_ip) + + # 4. security rules (filtering rules) + secgrp_refs = db.security_group_get_by_instance(context, instance_id) + + # 5. if any volume is mounted, prepare here. + if 0 != len(shelf_slots): + pass + + # 6. create nova-instance-instance-xxx in hypervisor through libvirt + # (This rule can be seen by executing virsh nwfilter-list) + self.driver.setup_nwfilters_for_instance(instance_ref) + + # 7. insert filtering rule + for secgrp_ref in secgrp_refs: + self.driver.refresh_security_group(secgrp_ref.id) + + # 8. vlan settings + self.network_manager.driver.ensure_vlan_bridge(network_ref['vlan'], + network_ref['bridge']) + + # created by masumotok + def nwfilter_for_instance_exists(self, context, instance_id): + """Check nova-instance-instance-xxx filter exists """ + instance_ref = db.instance_get(context, instance_id) + return self.driver.nwfilter_for_instance_exists(instance_ref) + + # created by masumotok + def live_migration(self, context, instance_id, dest): + """executes live migration.""" + + import time + # 1. ask dest host to preparing live migration. + compute_topic = db.queue_get_for(context, FLAGS.compute_topic, dest) + ret = rpc.call(context, + compute_topic, + {"method": "pre_live_migration", + "args": {'instance_id': instance_id, + 'dest': dest}}) + + if rpc.RemoteError == type(ret): + logging.error('Live migration failed(err at %s)', dest) + db.instance_set_state(context, + instance_id, + power_state.RUNNING, + 'running') + return + + # waiting for setting up nwfilter(nova-instance-instance-xxx) + # otherwise, live migration fail. + timeout_count = range(FLAGS.live_migration_timeout) + while 0 != len(timeout_count): + ret = rpc.call(context, + compute_topic, + {"method": "nwfilter_for_instance_exists", + "args": {'instance_id': instance_id}}) + if ret: + break + + timeout_count.pop() + time.sleep(1) + + if not ret: + logging.error('Timeout for pre_live_migration at %s', dest) + return + + # 2. executing live migration + # live_migration might raises ProcessExecution error, but + # nothing must be recovered in this version. + instance_ref = db.instance_get(context, instance_id) + ret = self.driver.live_migration(instance_ref, dest) + if not ret: + logging.debug('Fail to live migration') + return + + diff --git a/nova/db/api.py b/nova/db/api.py index 8f9dc2443..c62d5d6ef 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -195,6 +195,11 @@ def floating_ip_get_by_address(context, address): return IMPL.floating_ip_get_by_address(context, address) +# this method is created by masumotok +def floating_ip_update(context, address, values): + """update floating ip information.""" + return IMPL.floating_ip_update(context, address, values) + #################### @@ -334,6 +339,36 @@ def instance_add_security_group(context, instance_id, security_group_id): security_group_id) +# created by masumotok +def instance_get_all_by_host(context, hostname): + """Get instances by host""" + return IMPL.instance_get_all_by_host(context, hostname) + + +# created by masumotok +def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id): + """Get instances.vcpus by host and project""" + return IMPL.instance_get_vcpu_sum_by_host_and_project(context, + hostname, + proj_id) + + +# created by masumotok +def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): + """Get amount of memory by host and project """ + return IMPL.instance_get_memory_sum_by_host_and_project(context, + hostname, + proj_id) + + +# created by masumotok +def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id): + """Get total amount of disk by host and project """ + return IMPL.instance_get_disk_sum_by_host_and_project(context, + hostname, + proj_id) + + ################### @@ -833,3 +868,37 @@ def host_get_networks(context, host): """ return IMPL.host_get_networks(context, host) + + +# below all methods related to host table are created by masumotok +################### + + +def host_create(context, value): + """Create a host from the values dictionary.""" + return IMPL.host_create(context, value) + + +def host_get(context, host_id): + """Get an host or raise if it does not exist.""" + return IMPL.host_get(context, host_id) + + +def host_get_all(context, session=None): + """Get all hosts or raise if it does not exist.""" + return IMPL.host_get_all(context) + + +def host_get_by_name(context, host): + """Get an host or raise if it does not exist.""" + return IMPL.host_get_by_name(context, host) + + +def host_update(context, host, values): + """Set the given properties on an host and update it.""" + return IMPL.host_update(context, host, values) + + +def host_deactivated(context, host): + """set deleted flag to a given host""" + return IMPL.host_deactivated(context, host) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index dd9649054..811575765 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -394,6 +394,17 @@ def floating_ip_get_by_address(context, address, session=None): return result +# created by masumotok +@require_context +def floating_ip_update(context, address, values): + session = get_session() + with session.begin(): + floating_ip_ref = floating_ip_get_by_address(context, address, session) + for (key, value) in values.iteritems(): + floating_ip_ref[key] = value + floating_ip_ref.save(session=session) + + ################### @@ -746,6 +757,52 @@ def instance_add_security_group(context, instance_id, security_group_id): instance_ref.save(session=session) +# created by masumotok +def instance_get_all_by_host(context, hostname): + session = get_session() + if not session: + session = get_session() + + result = session.query(models.Instance + ).filter_by(host=hostname + ).filter_by(deleted=can_read_deleted(context) + ).all() + if None == result: + return [] + return result + + +# created by masumotok +def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id): + session = get_session() + + result = session.query(models.Instance + ).filter_by(host=hostname + ).filter_by(project_id=proj_id + ).filter_by(deleted=can_read_deleted(context) + ).value(column) + if None == result: + return 0 + return result + + +# created by masumotok +def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id): + return _instance_get_sum_by_host_and_project(context, 'vcpus', hostname, + proj_id) + + +# created by masumotok +def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): + return _instance_get_sum_by_host_and_project(context, 'memory_mb', + hostname, proj_id) + + +# created by masumotok +def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id): + return _instance_get_sum_by_host_and_project(context, 'local_gb', + hostname, proj_id) + ################### @@ -1746,3 +1803,77 @@ def host_get_networks(context, host): filter_by(deleted=False).\ filter_by(host=host).\ all() + + +#below all methods related to host table are created by masumotok +################### + +@require_admin_context +def host_create(context, values): + host_ref = models.Host() + for (key, value) in values.iteritems(): + host_ref[key] = value + host_ref.save() + return host_ref + + +@require_admin_context +def host_get(context, host_id, session=None): + if not session: + session = get_session() + + result = session.query(models.Host + ).filter_by(deleted=False + ).filter_by(id=host_id + ).first() + + if not result: + raise exception.NotFound('No host for id %s' % host_id) + + return result + + +@require_admin_context +def host_get_all(context, session=None): + if not session: + session = get_session() + + result = session.query(models.Host + ).filter_by(deleted=False + ).all() + + if not result: + raise exception.NotFound('No host record found .') + + return result + + +@require_admin_context +def host_get_by_name(context, host, session=None): + if not session: + session = get_session() + + result = session.query(models.Host + ).filter_by(deleted=False + ).filter_by(name=host + ).first() + + if not result: + raise exception.NotFound('No host for name %s' % host) + + return result + + +@require_admin_context +def host_update(context, host_id, values): + session = get_session() + with session.begin(): + host_ref = host_get(context, host_id, session=session) + for (key, value) in values.iteritems(): + host_ref[key] = value + host_ref.save(session=session) + + +@require_admin_context +def host_deactivated(context, host): + host_update(context, host, {'deleted': True}) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index fe0a9a921..16406f79a 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -138,6 +138,24 @@ class NovaBase(object): # __tablename__ = 'hosts' # id = Column(String(255), primary_key=True) +# this class is created by masumotok +class Host(BASE, NovaBase): + """Represents a host where services are running""" + __tablename__ = 'hosts' + id = Column(Integer, primary_key=True) + name = Column(String(255)) + cpu = Column(Integer, nullable=False, default=-1) + memory_mb = Column(Integer, nullable=False, default=-1) + hdd_gb = Column(Integer, nullable=False, default=-1) + #cpuid = Column(Integer, nullable=False) + deleted = Column(Boolean, default=False) + # C: when calling service_create() + # D: never deleted. instead of deleting cloumn "deleted" is true + # when host is down + # b/c Host.id is foreign key of service, and records + # of the "service" table are not deleted. + # R: Column "deleted" is true when calling hosts_up() and host is down. + class Service(BASE, NovaBase): """Represents a running service on a host.""" @@ -526,10 +544,14 @@ def register_models(): it will never need to be called explicitly elsewhere. """ from sqlalchemy import create_engine + #models = (Service, Instance, Volume, ExportDevice, IscsiTarget, FixedIp, + # FloatingIp, Network, SecurityGroup, + # SecurityGroupIngressRule, SecurityGroupInstanceAssociation, + # AuthToken, User, Project) # , Image, Host models = (Service, Instance, Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, - AuthToken, User, Project) # , Image, Host + AuthToken, User, Project, Host) # , Image engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: model.metadata.create_all(engine) diff --git a/nova/network/manager.py b/nova/network/manager.py index a7298b47f..e84b8a8f9 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -112,10 +112,14 @@ class NetworkManager(manager.Manager): # the designated network host. ctxt = context.get_admin_context() for network in self.db.host_get_networks(ctxt, self.host): + print '<<<<<< nova.network.manager.init_host <<<<' + print '<<<<<< nova.network.manager.init_host (%s)<<<<' % network['id'] self._on_set_network_host(ctxt, network['id']) def set_network_host(self, context, network_id): """Safely sets the host of the network.""" + print '<<<<<< nova.network.manager.set_network_host <<<<' + print '<<<<<< nova.network.manager.set_network_host (%s)<<<<' % network_id logging.debug("setting network host") host = self.db.network_set_host(context, network_id, @@ -452,6 +456,9 @@ class VlanManager(NetworkManager): self.driver.ensure_vlan_forward(network_ref['vpn_public_address'], network_ref['vpn_public_port'], network_ref['vpn_private_address']) + print '--------------------' + print 'UUUUUUPdate dhcp!' + print '--------------------' self.driver.update_dhcp(context, network_ref['id']) def setup_compute_network(self, context, instance_id): diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 60a3d2b4b..db8c3c30c 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -29,6 +29,10 @@ from nova import flags from nova import manager from nova import rpc from nova import utils +# 3 modules are added by masumotok +from nova import exception +from nova.api.ec2 import cloud +from nova.compute import power_state FLAGS = flags.FLAGS flags.DEFINE_string('scheduler_driver', @@ -66,3 +70,106 @@ class SchedulerManager(manager.Manager): {"method": method, "args": kwargs}) logging.debug("Casting to %s %s for %s", topic, host, method) + + # created by masumotok + def live_migration(self, context, ec2_id, dest): + """ live migration method""" + + # 1. get instance id + internal_id = cloud.ec2_id_to_internal_id(ec2_id) + instance_ref = db.instance_get_by_internal_id(context, internal_id) + instance_id = instance_ref['id'] + + # 2. check dst host still has enough capacities + if not self.has_enough_resource(context, instance_id, dest): + return False + + # 3. change instance_state + db.instance_set_state(context, + instance_id, + power_state.PAUSED, + 'migrating') + + # 4. request live migration + host = instance_ref['host'] + rpc.cast(context, + db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": 'live_migration', + "args": {'instance_id': instance_id, + 'dest': dest}}) + return True + + # this method is created by masumotok + def has_enough_resource(self, context, instance_id, dest): + + # get instance information + instance_ref = db.instance_get(context, instance_id) + ec2_id = instance_ref['hostname'] + vcpus = instance_ref['vcpus'] + mem = instance_ref['memory_mb'] + hdd = instance_ref['local_gb'] + + # get host information + host_ref = db.host_get_by_name(context, dest) + total_cpu = int(host_ref['cpu']) + total_mem = int(host_ref['memory_mb']) + total_hdd = int(host_ref['hdd_gb']) + + instances_ref = db.instance_get_all_by_host(context, dest) + for i_ref in instances_ref: + total_cpu -= int(i_ref['vcpus']) + total_mem -= int(i_ref['memory_mb']) + total_hdd -= int(i_ref['local_gb']) + + # check host has enough information + logging.debug('host(%s) remains vcpu:%s mem:%s hdd:%s,' % + (dest, total_cpu, total_mem, total_hdd)) + logging.debug('instance(%s) has vcpu:%s mem:%s hdd:%s,' % + (ec2_id, total_cpu, total_mem, total_hdd)) + + if total_cpu <= vcpus or total_mem <= mem or total_hdd <= hdd: + logging.debug('%s doesnt have enough resource for %s' % + (dest, ec2_id)) + return False + + logging.debug('%s has enough resource for %s' % (dest, ec2_id)) + return True + + # this method is created by masumotok + def show_host_resource(self, context, host, *args): + """ show the physical/usage resource given by hosts.""" + + try: + host_ref = db.host_get_by_name(context, host) + except exception.NotFound: + return {'ret': False, 'msg': 'No such Host'} + except: + raise + + # get physical resource information + h_resource = {'cpu': host_ref['cpu'], + 'memory_mb': host_ref['memory_mb'], + 'hdd_gb': host_ref['hdd_gb']} + + # get usage resource information + u_resource = {} + instances_ref = db.instance_get_all_by_host(context, host_ref['name']) + + if 0 == len(instances_ref): + return {'ret': True, 'phy_resource': h_resource, 'usage': {}} + + project_ids = [i['project_id'] for i in instances_ref] + project_ids = list(set(project_ids)) + for p_id in project_ids: + cpu = db.instance_get_vcpu_sum_by_host_and_project(context, + host, + p_id) + mem = db.instance_get_memory_sum_by_host_and_project(context, + host, + p_id) + hdd = db.instance_get_disk_sum_by_host_and_project(context, + host, + p_id) + u_resource[p_id] = {'cpu': cpu, 'memory_mb': mem, 'hdd_gb': hdd} + + return {'ret': True, 'phy_resource': h_resource, 'usage': u_resource} diff --git a/nova/service.py b/nova/service.py index 9454d4049..4b97062b4 100644 --- a/nova/service.py +++ b/nova/service.py @@ -72,6 +72,14 @@ class Service(object, service.Service): self.manager.init_host() self.model_disconnected = False ctxt = context.get_admin_context() + + # this try-except operations are added by masumotok + try: + host_ref = db.host_get_by_name(ctxt, self.host) + except exception.NotFound: + host_ref = db.host_create(ctxt, {'name': self.host}) + host_ref = self._update_host_ref(ctxt, host_ref) + try: service_ref = db.service_get_by_args(ctxt, self.host, @@ -109,6 +117,20 @@ class Service(object, service.Service): 'report_count': 0}) self.service_id = service_ref['id'] + # created by masumotok + def _update_host_ref(self, context, host_ref): + + if 0 <= self.manager_class_name.find('ComputeManager'): + cpu = self.manager.get_cpu_number() + memory_mb = self.manager.get_mem_size() + hdd_gb = self.manager.get_hdd_size() + db.host_update(context, + host_ref['id'], + {'cpu': cpu, + 'memory_mb': memory_mb, + 'hdd_gb': hdd_gb}) + return host_ref + def __getattr__(self, key): manager = self.__dict__.get('manager', None) return getattr(manager, key) diff --git a/nova/utils.py b/nova/utils.py index 142584df8..9e4ba6bc2 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -133,9 +133,16 @@ def runthis(prompt, cmd, check_exit_code=True): def generate_uid(topic, size=8): - characters = '01234567890abcdefghijklmnopqrstuvwxyz' - choices = [random.choice(characters) for x in xrange(size)] - return '%s-%s' % (topic, ''.join(choices)) + #modified by masumotok + #characters = '01234567890abcdefghijklmnopqrstuvwxyz' + #choices = [random.choice(characters) for x in xrange(size)] + #return '%s-%s' % (topic, ''.join(choices)) + if topic == "i": + return random.randint(0, 2 ** 28 - 1) + else: + characters = '01234567890abcdefghijklmnopqrstuvwxyz' + choices = [random.choice(characters) for x in xrange(size)] + return '%s-%s' % (topic, ''.join(choices)) def generate_mac(): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 18085089f..4ed791130 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -44,6 +44,8 @@ Supports KVM, QEMU, UML, and XEN. import logging import os import shutil +# appended by masumotok +#import libvirt import IPy from twisted.internet import defer @@ -101,6 +103,10 @@ flags.DEFINE_string('libvirt_uri', '', 'Override the default libvirt URI (which is dependent' ' on libvirt_type)') +# added by masumotok +flags.DEFINE_string('live_migration_uri', + "qemu+tcp://%s/system", + 'Define protocol used by live_migration feature') flags.DEFINE_bool('allow_project_net_traffic', True, 'Whether to allow in project network traffic') @@ -648,6 +654,101 @@ class LibvirtConnection(object): fw = NWFilterFirewall(self._conn) fw.ensure_security_group_filter(security_group_id) + # created by masumotok + def setup_nwfilters_for_instance(self, instance): + nwfilter = NWFilterFirewall(self._conn) + return nwfilter.setup_nwfilters_for_instance(instance) + + # created by masumotok + def nwfilter_for_instance_exists(self, instance_ref): + try: + filter = 'nova-instance-%s' % instance_ref.name + self._conn.nwfilterLookupByName(filter) + return True + except libvirt.libvirtError: + return False + + # created by masumotok + def live_migration(self, instance_ref, dest): + uri = FLAGS.live_migration_uri % dest + out, err = utils.execute("sudo virsh migrate --live %s %s" + % (instance_ref.name, uri)) + + # wait for completion of live_migration + d = defer.Deferred() + d.addCallback(lambda _: self._post_live_migration(instance_ref, dest)) + timer = task.LoopingCall(f=None) + + def _wait_for_live_migration(): + try: + state = self.get_info(instance_ref.name)['state'] + #except libvirt.libvirtError, e: + except exception.NotFound: + timer.stop() + d.callback(None) + timer.f = _wait_for_live_migration + timer.start(interval=0.5, now=True) + return d + + # created by masumotok + def _post_live_migration(self, instance_ref, dest): + + # 1. detaching volumes + # (not necessary in current version ) + #try : + # ec2_id = instance_ref['ec2_id'] + # volumes = db.volume_get_by_ec2_id(context, ec2_id) + # for volume in volumes : + # self.detach_volume(context, instance_id, volume.id) + #except exception.NotFound: + # logging.debug('%s doesnt mount any volumes.. ' % ec2_id) + + # 2. releasing vlan + # (not necessary in current implementation?) + + # 3. releasing security group ingress rule + # (not necessary in current implementation?) + + # 4. database updating + ec2_id = instance_ref['hostname'] + ctxt = context.get_admin_context() + + instance_id = instance_ref['id'] + fixed_ip = db.instance_get_fixed_address(ctxt, instance_id) + # not return if fixed_ip is not found, otherwise, + # instance never be accessible.. + if None == fixed_ip: + logging.error('fixed_ip is not found for %s ' % ec2_id) + db.fixed_ip_update(ctxt, fixed_ip, {'host': dest}) + network_ref = db.fixed_ip_get_network(ctxt, fixed_ip) + db.network_update(ctxt, network_ref['id'], {'host': dest}) + + try: + floating_ip = db.instance_get_floating_address(ctxt, instance_id) + # not return if floating_ip is not found, otherwise, + # instance never be accessible.. + if None == floating_ip: + logging.error('floating_ip is not found for %s ' % ec2_id) + floating_ip_ref = db.floating_ip_get_by_address(ctxt, floating_ip) + db.floating_ip_update(ctxt, + floating_ip_ref['address'], + {'host': dest}) + except exception.NotFound: + logging.debug('%s doesnt have floating_ip.. ' % ec2_id) + except: + msg = 'Live migration: Unexpected error:' + msg += '%s cannot inherit floating ip.. ' % ec2_id + logging.error(msg) + + db.instance_update(ctxt, + instance_id, + {'state_description': 'running', + 'state': power_state.RUNNING, + 'host': dest}) + + logging.info('Live migrating %s to %s finishes successfully' + % (ec2_id, dest)) + class NWFilterFirewall(object): """ -- cgit From 3313a5170a83feb6e571faa6296ffea7f065ec25 Mon Sep 17 00:00:00 2001 From: masumotok Date: Wed, 8 Dec 2010 17:21:04 +0900 Subject: コメントを除去 README.live_migration.txtのレビュー結果を修正 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/compute/api.py | 3 --- nova/compute/manager.py | 12 ------------ nova/db/api.py | 6 ------ nova/db/sqlalchemy/api.py | 7 ------- nova/network/manager.py | 7 ------- nova/scheduler/manager.py | 5 +---- nova/service.py | 2 -- nova/virt/libvirt_conn.py | 7 ------- 8 files changed, 1 insertion(+), 48 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 03922c272..929342a1e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -84,7 +84,6 @@ class ComputeAPI(base.Base): if not type(security_group) is list: security_group = [security_group] - print '<<<<<<<<<<<<<<<<<<<<<<<<<<1' security_groups = [] self.ensure_default_security_group(context) for security_group_name in security_group: @@ -93,7 +92,6 @@ class ComputeAPI(base.Base): security_group_name) security_groups.append(group['id']) - print '<<<<<<<<<<<<<<<<<<<<<<<<<<2' if key_data is None and key_name: key_pair = db.key_pair_get(context, context.user_id, key_name) key_data = key_pair['public_key'] @@ -117,7 +115,6 @@ class ComputeAPI(base.Base): 'key_name': key_name, 'key_data': key_data} - print '<<<<<<<<<<<<<<<<<<<<<<<<<<3' elevated = context.elevated() instances = [] logging.debug("Going to run %s instances...", num_instances) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4c42153b6..d271d17a4 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -36,11 +36,8 @@ terminating it. import datetime import logging -# added by masumotok import sys -# added by masumotok import traceback -# added by masumotok import os @@ -51,9 +48,7 @@ from nova import flags from nova import manager from nova import utils from nova.compute import power_state -# added by masumotok from nova import rpc -# added by masumotok from nova import db FLAGS = flags.FLAGS @@ -61,7 +56,6 @@ flags.DEFINE_string('instances_path', '$state_path/instances', 'where instances are stored on disk') flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection', 'Driver to use for controlling virtualization') -# created by masumotok flags.DEFINE_string('live_migration_timeout', 30, 'Timeout value for pre_live_migration is completed.') @@ -266,12 +260,10 @@ class ComputeManager(manager.Manager): self.db.volume_detached(context, volume_id) defer.returnValue(True) - # created by masumotok def get_cpu_number(self): """Get the number of physical computer cpu core .""" return open('/proc/cpuinfo').read().count('processor') - # created by masumotok def get_mem_size(self): """Get the memory size of physical computer .""" meminfo = open('/proc/meminfo').read().split() @@ -279,13 +271,11 @@ class ComputeManager(manager.Manager): # transforming kb to mb. return int(meminfo[idx + 1]) / 1024 - # created by masumotok def get_hdd_size(self): """Get the hdd size of physical computer .""" hddinfo = os.statvfs(FLAGS.instances_path) return hddinfo.f_bsize * hddinfo.f_blocks / 1024 / 1024 / 1024 - # created by masumotok def pre_live_migration(self, context, instance_id, dest): """Any preparation for live migration at dst host.""" @@ -334,13 +324,11 @@ class ComputeManager(manager.Manager): self.network_manager.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) - # created by masumotok def nwfilter_for_instance_exists(self, context, instance_id): """Check nova-instance-instance-xxx filter exists """ instance_ref = db.instance_get(context, instance_id) return self.driver.nwfilter_for_instance_exists(instance_ref) - # created by masumotok def live_migration(self, context, instance_id, dest): """executes live migration.""" diff --git a/nova/db/api.py b/nova/db/api.py index c62d5d6ef..71e8151e7 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -195,7 +195,6 @@ def floating_ip_get_by_address(context, address): return IMPL.floating_ip_get_by_address(context, address) -# this method is created by masumotok def floating_ip_update(context, address, values): """update floating ip information.""" return IMPL.floating_ip_update(context, address, values) @@ -339,13 +338,11 @@ def instance_add_security_group(context, instance_id, security_group_id): security_group_id) -# created by masumotok def instance_get_all_by_host(context, hostname): """Get instances by host""" return IMPL.instance_get_all_by_host(context, hostname) -# created by masumotok def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id): """Get instances.vcpus by host and project""" return IMPL.instance_get_vcpu_sum_by_host_and_project(context, @@ -353,7 +350,6 @@ def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id): proj_id) -# created by masumotok def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): """Get amount of memory by host and project """ return IMPL.instance_get_memory_sum_by_host_and_project(context, @@ -361,7 +357,6 @@ def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): proj_id) -# created by masumotok def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id): """Get total amount of disk by host and project """ return IMPL.instance_get_disk_sum_by_host_and_project(context, @@ -870,7 +865,6 @@ def host_get_networks(context, host): return IMPL.host_get_networks(context, host) -# below all methods related to host table are created by masumotok ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 811575765..45a10bc22 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -394,7 +394,6 @@ def floating_ip_get_by_address(context, address, session=None): return result -# created by masumotok @require_context def floating_ip_update(context, address, values): session = get_session() @@ -757,7 +756,6 @@ def instance_add_security_group(context, instance_id, security_group_id): instance_ref.save(session=session) -# created by masumotok def instance_get_all_by_host(context, hostname): session = get_session() if not session: @@ -772,7 +770,6 @@ def instance_get_all_by_host(context, hostname): return result -# created by masumotok def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id): session = get_session() @@ -786,19 +783,16 @@ def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id): return result -# created by masumotok def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id): return _instance_get_sum_by_host_and_project(context, 'vcpus', hostname, proj_id) -# created by masumotok def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): return _instance_get_sum_by_host_and_project(context, 'memory_mb', hostname, proj_id) -# created by masumotok def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id): return _instance_get_sum_by_host_and_project(context, 'local_gb', hostname, proj_id) @@ -1805,7 +1799,6 @@ def host_get_networks(context, host): all() -#below all methods related to host table are created by masumotok ################### @require_admin_context diff --git a/nova/network/manager.py b/nova/network/manager.py index e84b8a8f9..a7298b47f 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -112,14 +112,10 @@ class NetworkManager(manager.Manager): # the designated network host. ctxt = context.get_admin_context() for network in self.db.host_get_networks(ctxt, self.host): - print '<<<<<< nova.network.manager.init_host <<<<' - print '<<<<<< nova.network.manager.init_host (%s)<<<<' % network['id'] self._on_set_network_host(ctxt, network['id']) def set_network_host(self, context, network_id): """Safely sets the host of the network.""" - print '<<<<<< nova.network.manager.set_network_host <<<<' - print '<<<<<< nova.network.manager.set_network_host (%s)<<<<' % network_id logging.debug("setting network host") host = self.db.network_set_host(context, network_id, @@ -456,9 +452,6 @@ class VlanManager(NetworkManager): self.driver.ensure_vlan_forward(network_ref['vpn_public_address'], network_ref['vpn_public_port'], network_ref['vpn_private_address']) - print '--------------------' - print 'UUUUUUPdate dhcp!' - print '--------------------' self.driver.update_dhcp(context, network_ref['id']) def setup_compute_network(self, context, instance_id): diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index db8c3c30c..4345cfb0a 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -29,7 +29,6 @@ from nova import flags from nova import manager from nova import rpc from nova import utils -# 3 modules are added by masumotok from nova import exception from nova.api.ec2 import cloud from nova.compute import power_state @@ -71,7 +70,6 @@ class SchedulerManager(manager.Manager): "args": kwargs}) logging.debug("Casting to %s %s for %s", topic, host, method) - # created by masumotok def live_migration(self, context, ec2_id, dest): """ live migration method""" @@ -99,8 +97,8 @@ class SchedulerManager(manager.Manager): 'dest': dest}}) return True - # this method is created by masumotok def has_enough_resource(self, context, instance_id, dest): + """ check if destination host has enough resource for live migration""" # get instance information instance_ref = db.instance_get(context, instance_id) @@ -135,7 +133,6 @@ class SchedulerManager(manager.Manager): logging.debug('%s has enough resource for %s' % (dest, ec2_id)) return True - # this method is created by masumotok def show_host_resource(self, context, host, *args): """ show the physical/usage resource given by hosts.""" diff --git a/nova/service.py b/nova/service.py index 4b97062b4..648293cea 100644 --- a/nova/service.py +++ b/nova/service.py @@ -73,7 +73,6 @@ class Service(object, service.Service): self.model_disconnected = False ctxt = context.get_admin_context() - # this try-except operations are added by masumotok try: host_ref = db.host_get_by_name(ctxt, self.host) except exception.NotFound: @@ -117,7 +116,6 @@ class Service(object, service.Service): 'report_count': 0}) self.service_id = service_ref['id'] - # created by masumotok def _update_host_ref(self, context, host_ref): if 0 <= self.manager_class_name.find('ComputeManager'): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4ed791130..783f2409e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -44,8 +44,6 @@ Supports KVM, QEMU, UML, and XEN. import logging import os import shutil -# appended by masumotok -#import libvirt import IPy from twisted.internet import defer @@ -103,7 +101,6 @@ flags.DEFINE_string('libvirt_uri', '', 'Override the default libvirt URI (which is dependent' ' on libvirt_type)') -# added by masumotok flags.DEFINE_string('live_migration_uri', "qemu+tcp://%s/system", 'Define protocol used by live_migration feature') @@ -654,12 +651,10 @@ class LibvirtConnection(object): fw = NWFilterFirewall(self._conn) fw.ensure_security_group_filter(security_group_id) - # created by masumotok def setup_nwfilters_for_instance(self, instance): nwfilter = NWFilterFirewall(self._conn) return nwfilter.setup_nwfilters_for_instance(instance) - # created by masumotok def nwfilter_for_instance_exists(self, instance_ref): try: filter = 'nova-instance-%s' % instance_ref.name @@ -668,7 +663,6 @@ class LibvirtConnection(object): except libvirt.libvirtError: return False - # created by masumotok def live_migration(self, instance_ref, dest): uri = FLAGS.live_migration_uri % dest out, err = utils.execute("sudo virsh migrate --live %s %s" @@ -690,7 +684,6 @@ class LibvirtConnection(object): timer.start(interval=0.5, now=True) return d - # created by masumotok def _post_live_migration(self, instance_ref, dest): # 1. detaching volumes -- cgit From 4809c1bf82130f969614a8f0458636a462b81a88 Mon Sep 17 00:00:00 2001 From: masumotok Date: Thu, 16 Dec 2010 18:20:04 +0900 Subject: Hostテーブルのカラム名を修正 FlatManager, FlatDHCPManagerに対応 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/compute/manager.py | 32 +++++++++++--------------------- nova/db/sqlalchemy/api.py | 6 ++++++ nova/db/sqlalchemy/models.py | 4 ++-- nova/network/manager.py | 15 +++++++++------ nova/scheduler/manager.py | 14 ++++++++------ nova/service.py | 8 ++++---- nova/virt/libvirt_conn.py | 4 ++++ 7 files changed, 44 insertions(+), 39 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d271d17a4..81cca7770 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -260,9 +260,9 @@ class ComputeManager(manager.Manager): self.db.volume_detached(context, volume_id) defer.returnValue(True) - def get_cpu_number(self): - """Get the number of physical computer cpu core .""" - return open('/proc/cpuinfo').read().count('processor') + def get_vcpu_number(self): + """Get the number of vcpu on physical computer.""" + return self.driver.get_vcpu_number() def get_mem_size(self): """Get the memory size of physical computer .""" @@ -302,27 +302,19 @@ class ComputeManager(manager.Manager): ''.join(traceback.format_tb(sys.exc_info()[2]))) return - # 3. getting network refs - network_ref = db.fixed_ip_get_network(context, fixed_ip) - - # 4. security rules (filtering rules) - secgrp_refs = db.security_group_get_by_instance(context, instance_id) - - # 5. if any volume is mounted, prepare here. + # 3. if any volume is mounted, prepare here. if 0 != len(shelf_slots): pass - # 6. create nova-instance-instance-xxx in hypervisor through libvirt - # (This rule can be seen by executing virsh nwfilter-list) + # 4. Creating nova-instance-instance-xxx, this is written to libvirt.xml, + # and can be seen when executin "virsh nwfiter-list" On destination host, + # this nwfilter is necessary. + # In addition this method is creating security rule ingress rule onto + # destination host. self.driver.setup_nwfilters_for_instance(instance_ref) - # 7. insert filtering rule - for secgrp_ref in secgrp_refs: - self.driver.refresh_security_group(secgrp_ref.id) - - # 8. vlan settings - self.network_manager.driver.ensure_vlan_bridge(network_ref['vlan'], - network_ref['bridge']) + # 5. bridge settings + self.network_manager.setup_compute_network(instance_id) def nwfilter_for_instance_exists(self, context, instance_id): """Check nova-instance-instance-xxx filter exists """ @@ -375,5 +367,3 @@ class ComputeManager(manager.Manager): if not ret: logging.debug('Fail to live migration') return - - diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 45a10bc22..e4792fe23 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -744,6 +744,7 @@ def instance_update(context, instance_id, values): instance_ref.save(session=session) +@require_context def instance_add_security_group(context, instance_id, security_group_id): """Associate the given security group with the given instance""" session = get_session() @@ -756,6 +757,7 @@ def instance_add_security_group(context, instance_id, security_group_id): instance_ref.save(session=session) +@require_context def instance_get_all_by_host(context, hostname): session = get_session() if not session: @@ -770,6 +772,7 @@ def instance_get_all_by_host(context, hostname): return result +@require_context def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id): session = get_session() @@ -783,16 +786,19 @@ def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id): return result +@require_context def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id): return _instance_get_sum_by_host_and_project(context, 'vcpus', hostname, proj_id) +@require_context def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): return _instance_get_sum_by_host_and_project(context, 'memory_mb', hostname, proj_id) +@require_context def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id): return _instance_get_sum_by_host_and_project(context, 'local_gb', hostname, proj_id) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 16406f79a..db6f51948 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -144,9 +144,9 @@ class Host(BASE, NovaBase): __tablename__ = 'hosts' id = Column(Integer, primary_key=True) name = Column(String(255)) - cpu = Column(Integer, nullable=False, default=-1) + vcpus = Column(Integer, nullable=False, default=-1) memory_mb = Column(Integer, nullable=False, default=-1) - hdd_gb = Column(Integer, nullable=False, default=-1) + local_gb = Column(Integer, nullable=False, default=-1) #cpuid = Column(Integer, nullable=False) deleted = Column(Boolean, default=False) # C: when calling service_create() diff --git a/nova/network/manager.py b/nova/network/manager.py index a7298b47f..a08b6094d 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -139,7 +139,7 @@ class NetworkManager(manager.Manager): """Called when this host becomes the host for a network.""" raise NotImplementedError() - def setup_compute_network(self, context, instance_id): + def setup_compute_network(self, context, instance_id, network_ref=None): """Sets up matching network for compute hosts.""" raise NotImplementedError() @@ -298,7 +298,7 @@ class FlatManager(NetworkManager): self.db.fixed_ip_update(context, address, {'allocated': False}) self.db.fixed_ip_disassociate(context.elevated(), address) - def setup_compute_network(self, context, instance_id): + def setup_compute_network(self, context, instance_id, network_ref=None): """Network is created manually.""" pass @@ -358,9 +358,10 @@ class FlatDHCPManager(FlatManager): super(FlatDHCPManager, self).init_host() self.driver.metadata_forward() - def setup_compute_network(self, context, instance_id): + def setup_compute_network(self, context, instance_id, network_ref=None): """Sets up matching network for compute hosts.""" - network_ref = db.network_get_by_instance(context, instance_id) + if network_ref is None: + network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_bridge(network_ref['bridge'], FLAGS.flat_interface, network_ref) @@ -454,9 +455,11 @@ class VlanManager(NetworkManager): network_ref['vpn_private_address']) self.driver.update_dhcp(context, network_ref['id']) - def setup_compute_network(self, context, instance_id): + + def setup_compute_network(self, context, instance_id, network_ref=None): """Sets up matching network for compute hosts.""" - network_ref = db.network_get_by_instance(context, instance_id) + if network_ref is None: + network_ref = db.network_get_by_instance(context, instance_id) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge']) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 4345cfb0a..d36525506 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -109,9 +109,9 @@ class SchedulerManager(manager.Manager): # get host information host_ref = db.host_get_by_name(context, dest) - total_cpu = int(host_ref['cpu']) + total_cpu = int(host_ref['vcpus']) total_mem = int(host_ref['memory_mb']) - total_hdd = int(host_ref['hdd_gb']) + total_hdd = int(host_ref['local_gb']) instances_ref = db.instance_get_all_by_host(context, dest) for i_ref in instances_ref: @@ -144,9 +144,9 @@ class SchedulerManager(manager.Manager): raise # get physical resource information - h_resource = {'cpu': host_ref['cpu'], + h_resource = {'vcpus': host_ref['vcpus'], 'memory_mb': host_ref['memory_mb'], - 'hdd_gb': host_ref['hdd_gb']} + 'local_gb': host_ref['local_gb']} # get usage resource information u_resource = {} @@ -158,7 +158,7 @@ class SchedulerManager(manager.Manager): project_ids = [i['project_id'] for i in instances_ref] project_ids = list(set(project_ids)) for p_id in project_ids: - cpu = db.instance_get_vcpu_sum_by_host_and_project(context, + vcpus = db.instance_get_vcpu_sum_by_host_and_project(context, host, p_id) mem = db.instance_get_memory_sum_by_host_and_project(context, @@ -167,6 +167,8 @@ class SchedulerManager(manager.Manager): hdd = db.instance_get_disk_sum_by_host_and_project(context, host, p_id) - u_resource[p_id] = {'cpu': cpu, 'memory_mb': mem, 'hdd_gb': hdd} + u_resource[p_id] = {'vcpus': vcpus, + 'memory_mb': mem, + 'local_gb': hdd} return {'ret': True, 'phy_resource': h_resource, 'usage': u_resource} diff --git a/nova/service.py b/nova/service.py index 648293cea..3ce07a3e0 100644 --- a/nova/service.py +++ b/nova/service.py @@ -119,14 +119,14 @@ class Service(object, service.Service): def _update_host_ref(self, context, host_ref): if 0 <= self.manager_class_name.find('ComputeManager'): - cpu = self.manager.get_cpu_number() + cpu = self.manager.get_vcpu_number() memory_mb = self.manager.get_mem_size() - hdd_gb = self.manager.get_hdd_size() + local_gb = self.manager.get_hdd_size() db.host_update(context, host_ref['id'], - {'cpu': cpu, + {'vcpus': cpu, 'memory_mb': memory_mb, - 'hdd_gb': hdd_gb}) + 'local_gb': local_gb}) return host_ref def __getattr__(self, key): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 783f2409e..f2b5cf794 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -631,6 +631,10 @@ class LibvirtConnection(object): return interfaces + def get_vcpu_number(self): + """ get vcpu number of physical computer """ + return self._conn.getMaxVcpus(None) + def block_stats(self, instance_name, disk): """ Note that this function takes an instance name, not an Instance, so -- cgit From 70f1f0d8c7a7214c5b6683c0be863cdbf0f060af Mon Sep 17 00:00:00 2001 From: masumotok Date: Mon, 20 Dec 2010 08:03:25 +0900 Subject: テストコードをレポジトリに追加 nova.compute.manager.pre_live_migration()について、異常終了しているのに正常終了の戻り値を返すことがあったため変更 - 正常終了の戻り値をTrueに変更 - fixed_ipが見つからないときにはRemoteErrorをraiseする - それに合わせてnova.compute.manager.live_migrationも変更 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/compute/manager.py | 13 +- nova/livemigration_test/SI/picture.pptx | Bin 0 -> 137730 bytes nova/livemigration_test/SI/testCase_SI.xls | Bin 0 -> 35840 bytes .../SI/testParameterSheet_SI.xls | Bin 0 -> 464384 bytes nova/livemigration_test/SI/utils/demo-firstboot.sh | 39 ++ .../SI/utils/demo-runInstance.sh | 57 +++ nova/livemigration_test/SI/utils/nova-manage.conf | 18 + nova/livemigration_test/SI/utils/nova.conf | 10 + nova/livemigration_test/SI/utils/nova.sh | 180 +++++++++ nova/livemigration_test/SI/utils/nova.sh.compute | 37 ++ nova/livemigration_test/UT/computeManager.test.py | 407 +++++++++++++++++++++ .../UT/libvirtConnection.test.py | 366 ++++++++++++++++++ nova/livemigration_test/UT/nova-manage.test.py | 318 ++++++++++++++++ .../livemigration_test/UT/schedulerManager.test.py | 360 ++++++++++++++++++ nova/livemigration_test/UT/testCase_UT.xls | Bin 0 -> 195072 bytes 15 files changed, 1799 insertions(+), 6 deletions(-) create mode 100644 nova/livemigration_test/SI/picture.pptx create mode 100644 nova/livemigration_test/SI/testCase_SI.xls create mode 100644 nova/livemigration_test/SI/testParameterSheet_SI.xls create mode 100755 nova/livemigration_test/SI/utils/demo-firstboot.sh create mode 100755 nova/livemigration_test/SI/utils/demo-runInstance.sh create mode 100644 nova/livemigration_test/SI/utils/nova-manage.conf create mode 100644 nova/livemigration_test/SI/utils/nova.conf create mode 100755 nova/livemigration_test/SI/utils/nova.sh create mode 100755 nova/livemigration_test/SI/utils/nova.sh.compute create mode 100644 nova/livemigration_test/UT/computeManager.test.py create mode 100644 nova/livemigration_test/UT/libvirtConnection.test.py create mode 100644 nova/livemigration_test/UT/nova-manage.test.py create mode 100644 nova/livemigration_test/UT/schedulerManager.test.py create mode 100644 nova/livemigration_test/UT/testCase_UT.xls (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 81cca7770..bad525115 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -39,6 +39,7 @@ import logging import sys import traceback import os +import time from twisted.internet import defer @@ -297,10 +298,10 @@ class ComputeManager(manager.Manager): # 2. getting fixed ips fixed_ip = db.instance_get_fixed_address(context, instance_id) if None == fixed_ip: - logging.error('Not found fixedip for %s\n%s', - ec2_id, - ''.join(traceback.format_tb(sys.exc_info()[2]))) - return + exc_type = 'NotFoundError' + val = '%s(%s) doesnt have fixed_ip ' % (instance_id, ec2_id) + tb = ''.join(traceback.format_tb(sys.exc_info()[2])) + raise rpc.RemoteError(exc_type, val, tb) # 3. if any volume is mounted, prepare here. if 0 != len(shelf_slots): @@ -315,6 +316,7 @@ class ComputeManager(manager.Manager): # 5. bridge settings self.network_manager.setup_compute_network(instance_id) + return True def nwfilter_for_instance_exists(self, context, instance_id): """Check nova-instance-instance-xxx filter exists """ @@ -324,7 +326,6 @@ class ComputeManager(manager.Manager): def live_migration(self, context, instance_id, dest): """executes live migration.""" - import time # 1. ask dest host to preparing live migration. compute_topic = db.queue_get_for(context, FLAGS.compute_topic, dest) ret = rpc.call(context, @@ -333,7 +334,7 @@ class ComputeManager(manager.Manager): "args": {'instance_id': instance_id, 'dest': dest}}) - if rpc.RemoteError == type(ret): + if True != ret: logging.error('Live migration failed(err at %s)', dest) db.instance_set_state(context, instance_id, diff --git a/nova/livemigration_test/SI/picture.pptx b/nova/livemigration_test/SI/picture.pptx new file mode 100644 index 000000000..b47bec9b5 Binary files /dev/null and b/nova/livemigration_test/SI/picture.pptx differ diff --git a/nova/livemigration_test/SI/testCase_SI.xls b/nova/livemigration_test/SI/testCase_SI.xls new file mode 100644 index 000000000..723363c1e Binary files /dev/null and b/nova/livemigration_test/SI/testCase_SI.xls differ diff --git a/nova/livemigration_test/SI/testParameterSheet_SI.xls b/nova/livemigration_test/SI/testParameterSheet_SI.xls new file mode 100644 index 000000000..192d9705b Binary files /dev/null and b/nova/livemigration_test/SI/testParameterSheet_SI.xls differ diff --git a/nova/livemigration_test/SI/utils/demo-firstboot.sh b/nova/livemigration_test/SI/utils/demo-firstboot.sh new file mode 100755 index 000000000..3a6f7fb0b --- /dev/null +++ b/nova/livemigration_test/SI/utils/demo-firstboot.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +DIR=/opt/nova-2010.1 + +# 1. 管理者ユーザを作成する +# nova-manage user admin ユーザ名 access-key secret-key +# +#$DIR/bin/nova-manage user admin admin admin admin + +# 2. プロジェクトを作成する +# nova-manage create project プロジェクト名 プロジェクトに属するユーザ名 +# +#$DIR/bin/nova-manage project create admin admin + +# 3. クラウドを使うための認証情報を生成する +# nova-manage project environment プロジェクト名 ユーザ名 認証情報を格納するファイル +# +#$DIR/bin/nova-manage project environment admin admin $DIR/novarc + +# 4. 認証情報の読み込み +. $DIR/novarc + +# 5. プロジェクト用仮想マシンネットワークの作成を行う +# nova-manage user admin ユーザ名 access-key secret-key +# +$DIR/bin/nova-manage network create 10.0.0.0/8 3 16 + +# 6. 初回ログインにはSSHの公開鍵認証が必要 +# +if [ "" == "`euca-describe-keypairs | grep testkey`" ]; then + euca-add-keypair testkey > testkey.pem +fi + +# 7. +for i in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do + sudo ip addr del $i dev eth0 2> /dev/null +done + + diff --git a/nova/livemigration_test/SI/utils/demo-runInstance.sh b/nova/livemigration_test/SI/utils/demo-runInstance.sh new file mode 100755 index 000000000..171291262 --- /dev/null +++ b/nova/livemigration_test/SI/utils/demo-runInstance.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +DIR=/opt/nova-2010.1 + +function inc_assigned(){ + assigned=`expr $assigned + 1` +} + + +# 1. 認証情報の読み込み +. $DIR/novarc + +# 3. 仮想マシンの起動 +# +ret=`euca-run-instances -t m1.small -k testkey ami-centos` +#ret=`euca-run-instances -t m1.small -k testkey ami-tiny` + +# 4. 仮想マシン用IPの確保 +# 未登録なら登録しておく +registered=`euca-describe-addresses` +for ip in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do + + not_registered=`echo $registered | grep $ip` + if [ "" == "$not_registered" ]; then + echo "[INFO] registed $ip" + $DIR/bin/nova-manage floating create `hostname` $ip + fi +done + +# 5. IPの割当 +echo 0 > /tmp/demo-runinstance +euca-describe-addresses | grep -v reserved | while read line; do + # 割り当てられてないものを仮想マシンに割り当てる + ip=`echo $line | cut -d ' ' -f 2` + id=`echo $ret | cut -d ' ' -f 5` + if [ "" == "`echo $id | grep i- `" ] ; then + echo "[INFO] try again" $ret + break + fi + echo "[INFO] assigned to ipaddr($ip) to instance($id) " + euca-associate-address -i $id $ip + echo 1 > /tmp/demo-runinstance + break +done + +echo $assigned +if [ 0 -eq "`cat /tmp/demo-runinstance`" ] ; then + echo "[INFO] address is full." +fi +rm -rf /tmp/demo-runinstance + + +# 6. FWの設定 +euca-authorize -P tcp -p 22 default 2> /dev/null > /dev/null +euca-authorize -P tcp -p 80 default 2> /dev/null > /dev/null +euca-authorize -P tcp -p 5555 default 2> /dev/null > /dev/null + diff --git a/nova/livemigration_test/SI/utils/nova-manage.conf b/nova/livemigration_test/SI/utils/nova-manage.conf new file mode 100644 index 000000000..9f8a02b96 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova-manage.conf @@ -0,0 +1,18 @@ +--verbose +--nodaemon +--dhcpbridge_flagfile=/etc/nova/nova-manage.conf +--FAKE_subdomain=ec2 +--libvirt_type=qemu +--no_internet_conn=True +--public_netif=eth0 +--public_interface=eth0 + +--cc-host=172.19.0.131 +--routing_source_ip=172.19.0.131 +--sql_connection=mysql://root:nova@172.19.0.131/nova +--rabbit_host=172.19.0.131 +--redis_host=172.19.0.131 +--s3_host=172.19.0.131 +--auth_driver=nova.auth.ldapdriver.LdapDriver +--ldap_url=ldap://172.19.0.131 + diff --git a/nova/livemigration_test/SI/utils/nova.conf b/nova/livemigration_test/SI/utils/nova.conf new file mode 100644 index 000000000..c66bfbc53 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova.conf @@ -0,0 +1,10 @@ +--verbose +--nodaemon +--dhcpbridge_flagfile=/opt/nova-2010.4//bin/nova.conf +--network_manager=nova.network.manager.VlanManager +--cc_host=172.19.0.131 +--routing_source_ip=172.19.0.131 +--sql_connection=mysql://root:nova@localhost/nova +--auth_driver=nova.auth.ldapdriver.LdapDriver +--libvirt_type=qemu +--public_interface=eth0 diff --git a/nova/livemigration_test/SI/utils/nova.sh b/nova/livemigration_test/SI/utils/nova.sh new file mode 100755 index 000000000..b8e2e9f26 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +DIR=`pwd` +CMD=$1 +SOURCE_BRANCH=lp:nova +if [ -n "$2" ]; then + SOURCE_BRANCH=$2 +fi +#DIRNAME=nova +DIRNAME="" +NOVA_DIR=$DIR/$DIRNAME +if [ -n "$3" ]; then + NOVA_DIR=$DIR/$3 +fi + +if [ ! -n "$HOST_IP" ]; then + # NOTE(vish): This will just get the first ip in the list, so if you + # have more than one eth device set up, this will fail, and + # you should explicitly set HOST_IP in your environment + HOST_IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` +fi + +#USE_MYSQL=${USE_MYSQL:-0} +USE_MYSQL=1 +MYSQL_PASS=${MYSQL_PASS:-nova} +TEST=${TEST:-0} +#USE_LDAP=${USE_LDAP:-0} +USE_LDAP=1 +LIBVIRT_TYPE=${LIBVIRT_TYPE:-qemu} +NET_MAN=${NET_MAN:-VlanManager} +# NOTE(vish): If you are using FlatDHCP on multiple hosts, set the interface +# below but make sure that the interface doesn't already have an +# ip or you risk breaking things. +# FLAT_INTERFACE=eth0 + +if [ "$USE_MYSQL" == 1 ]; then + SQL_CONN=mysql://root:$MYSQL_PASS@localhost/nova +else + SQL_CONN=sqlite:///$NOVA_DIR/nova.sqlite +fi + +if [ "$USE_LDAP" == 1 ]; then + AUTH=ldapdriver.LdapDriver +else + AUTH=dbdriver.DbDriver +fi + +mkdir -p /etc/nova +cat >$NOVA_DIR/bin/nova.conf << NOVA_CONF_EOF +--verbose +--nodaemon +--dhcpbridge_flagfile=$NOVA_DIR/bin/nova.conf +--network_manager=nova.network.manager.$NET_MAN +--cc_host=$HOST_IP +--routing_source_ip=$HOST_IP +--sql_connection=$SQL_CONN +--auth_driver=nova.auth.$AUTH +--libvirt_type=$LIBVIRT_TYPE +--public_interface=eth0 +NOVA_CONF_EOF + +if [ -n "$FLAT_INTERFACE" ]; then + echo "--flat_interface=$FLAT_INTERFACE" >>$NOVA_DIR/bin/nova.conf +fi + +if [ "$CMD" == "branch" ]; then + sudo apt-get install -y bzr + rm -rf $NOVA_DIR + bzr branch $SOURCE_BRANCH $NOVA_DIR + cd $NOVA_DIR + mkdir -p $NOVA_DIR/instances + mkdir -p $NOVA_DIR/networks +fi + +# You should only have to run this once +if [ "$CMD" == "install" ]; then + sudo apt-get install -y python-software-properties + sudo add-apt-repository ppa:nova-core/ppa + sudo apt-get update + sudo apt-get install -y dnsmasq kpartx kvm gawk iptables ebtables + sudo apt-get install -y user-mode-linux kvm libvirt-bin + sudo apt-get install -y screen euca2ools vlan curl rabbitmq-server + sudo apt-get install -y lvm2 iscsitarget open-iscsi + echo "ISCSITARGET_ENABLE=true" | sudo tee /etc/default/iscsitarget + sudo /etc/init.d/iscsitarget restart + sudo modprobe kvm + sudo /etc/init.d/libvirt-bin restart + sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot + sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy + sudo apt-get install -y python-libvirt python-libxml2 python-routes + if [ "$USE_MYSQL" == 1 ]; then + cat </etc/nova/nova-manage.conf << NOVA_CONF_EOF +--verbose +--nodaemon +--dhcpbridge_flagfile=/etc/nova/nova-manage.conf +--FAKE_subdomain=ec2 +--libvirt_type=qemu +--no_internet_conn=True +--public_netif=eth0 +--public_interface=eth0 + +--cc-host=$HOST_IP +--routing_source_ip=$HOST_IP +--sql_connection=mysql://root:nova@$HOST_IP/nova +--rabbit_host=$HOST_IP +--redis_host=$HOST_IP +--s3_host=$HOST_IP +--auth_driver=nova.auth.ldapdriver.LdapDriver +--ldap_url=ldap://$HOST_IP + +NOVA_CONF_EOF + +$DIR/bin/nova-compute --flagfile=/etc/nova/nova-manage.conf + diff --git a/nova/livemigration_test/UT/computeManager.test.py b/nova/livemigration_test/UT/computeManager.test.py new file mode 100644 index 000000000..d28d3ccb6 --- /dev/null +++ b/nova/livemigration_test/UT/computeManager.test.py @@ -0,0 +1,407 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR = '/opt/openstack/nova' +#NOVA_DIR = '/opt/nova-2010.4' + +import sys +import unittest +import commands +import re +import logging + +from mock import Mock +import twisted + +try: + print + print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' \ + % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.compute.manager import ComputeManager + from nova.virt.libvirt_conn import LibvirtConnection + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + + def write(self, arg): + self.buffer += arg + + def writelines(self, arg): + self.buffer += arg + + def flush(self): + print 'flush' + self.buffer = '' + + +class tmpStderr(tmpStdout): + def write(self, arg): + self.buffer += arg + + def flush(self): + pass + + def realFlush(self): + self.buffer = '' + +dummyCallReturnValue={ 0:True } +dummyCallCount=0 +def dummyCall(context, topic, method): + global dummyCallReturnValue, dummyCallCount + if dummyCallCount in dummyCallReturnValue.keys() : + ret = dummyCallReturnValue[ dummyCallCount ] + dummyCallCount += 1 + return ret + else : + dummyCallCount += 1 + return False + + +class ComputeTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + stderr = None + stderrBak = None + manager = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + #if self.stdout is None: + # self.__class__.stdout = tmpStdout() + #self.stdoutBak = sys.stdout + #sys.stdout = self.stdout + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + + self.host = 'openstack2-api' + if self.manager is None: + self.__class__.manager = ComputeManager(host=self.host) + + self.setTestData() + self.setMocks() + + def setTestData(self): + + self.host1 = Host() + for key, val in [('name', 'host1'), ('cpu', 5), + ('memory_mb', 20480), ('hdd_gb', 876)]: + self.host1.__setitem__(key, val) + + self.host2 = Host() + for key, val in [('name', 'host2'), ('cpu', 5), + ('memory_mb', 20480), ('hdd_gb', 876)]: + self.host2.__setitem__(key, val) + + self.instance1 = Instance() + for key, val in [('id', 1), ('host', 'host1'), + ('hostname', 'i-12345'), ('state', power_state.RUNNING), + ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), + ('hdd_gb', 5), ('internal_id', 12345)]: + self.instance1.__setitem__(key, val) + + self.instance2 = Instance() + for key, val in [('id', 2), ('host', 'host1'), + ('hostname', 'i-12345'), ('state', power_state.RUNNING), + ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), + ('hdd_gb', 5)]: + self.instance2.__setitem__(key, val) + + self.fixed_ip1 = FixedIp() + for key, val in [('id', 1), ('address', '1.1.1.1'), + ('network_id', '1'), ('instance_id', 1)]: + self.fixed_ip1.__setitem__(key, val) + + self.vol1 = Volume() + for key, val in [('id', 1), ('ec2_id', 'vol-qijjuc7e'), + ('availability_zone', 'nova'), ('host', 'host1')]: + self.vol1.__setitem__(key, val) + + self.vol2 = Volume() + for key, val in [('id', 2), ('ec2_id', 'vol-qi22222'), + ('availability_zone', 'nova'), ('host', 'host1')]: + self.vol2.__setitem__(key, val) + + self.secgrp1 = Volume() + for key, val in [('id', 1), ('ec2_id', 'default')]: + self.secgrp1.__setitem__(key, val) + + self.secgrp2 = Volume() + for key, val in [('id', 2), ('ec2_id', 'def2')]: + self.secgrp2.__setitem__(key, val) + + self.netref1 = Network() + + def setMocks(self): + + # mocks for pre_live_migration + self.ctxt = context.get_admin_context() + db.instance_get = Mock(return_value=self.instance1) + db.volume_get_by_ec2_id = Mock(return_value=[self.vol1, self.vol2]) + db.volume_get_shelf_and_blade = Mock(return_value=(3, 4)) + db.instance_get_fixed_address = Mock(return_value=self.fixed_ip1) + db.security_group_get_by_instance \ + = Mock(return_value=[self.secgrp1, self.secgrp2]) + self.manager.driver.setup_nwfilters_for_instance \ + = Mock(return_value=None) + self.manager.driver.nwfilter_for_instance_exists = Mock(return_value=None) + self.manager.network_manager.setup_compute_network \ + = Mock(return_value=None) + # mocks for live_migration_ + rpc.call = Mock(return_value=True) + db.instance_set_state = Mock(return_value=True) + + # ---> test for nova.compute.manager.pre_live_migration() + def test01(self): + """01: NotFound error occurs on finding instance on DB. """ + + db.instance_get = Mock(side_effect=exception.NotFound('ERR')) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test02(self): + """02: NotAuthrized occurs on finding volume on DB. """ + + db.volume_get_by_ec2_id \ + = Mock(side_effect=exception.NotAuthorized('ERR')) + + self.assertRaises(exception.NotAuthorized, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test03(self): + """03: Unexpected exception occurs on finding volume on DB. """ + + db.volume_get_by_ec2_id = Mock(side_effect=TypeError('ERR')) + + self.assertRaises(TypeError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test04(self): + """04: no volume and fixed ip found on DB, """ + + db.volume_get_by_ec2_id = Mock(side_effect=exception.NotFound('ERR')) + db.instance_get_fixed_address = Mock(return_value=None) + + self.assertRaises(rpc.RemoteError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + c1 = (0 <= sys.stderr.buffer.find('has no volume')) + + self.assertEqual(c1, True) + + def test05(self): + """05: volume found and no fixed_ip found on DB. """ + + db.instance_get_fixed_address \ + = Mock(side_effect=exception.NotFound('ERR')) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test06(self): + """06: self.driver.setup_nwfilters_for_instance causes NotFound. """ + self.manager.driver.setup_nwfilters_for_instance \ + = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test07(self): + """07: self.network_manager.setup_compute_network causes ProcessExecutionError. """ + self.manager.network_manager.setup_compute_network \ + = Mock(side_effect=exception.ProcessExecutionError("ERR")) + + self.assertRaises(exception.ProcessExecutionError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + + def test08(self): + """08: self.manager.network_manager.setup_compute_network + exception.NotFound. """ + self.manager.network_manager.setup_compute_network \ + = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + # those 2 cases are omitted : + # self.driver.setup_nwfilters_for_instance causes + # twisted.python.failure.Failure. + # self.driver.refresh_security_group causes twisted.python.failure.Failure. + # + # twisted.python.failure.Failure can not be used with assertRaises, + # it doesnt have __call___ + # + + def test09(self): + """09: volume/fixed_ip found on DB, all procedure finish + successfully.. """ + + result = self.manager.pre_live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + self.assertEqual(result, True) + + # ---> test for nova.compute.manager.live_migration() + + def test10(self): + """10: rpc.call(pre_live_migration returns Error(Not None). """ + rpc.call = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test11(self): + """11: if rpc.call returns rpc.RemoteError. """ + + rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) + db.instance_set_state = Mock(return_value=True) + result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + c1 = (None == result) + c2 = (0 <= sys.stderr.buffer.find('err at')) + self.assertEqual(c1 and c2, True) + + def test12(self): + """12: if rpc.call returns rpc.RemoteError and instance_set_state + also ends up err. (then , unexpected err occurs, in this case + TypeError) + """ + rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) + db.instance_set_state = Mock(side_effect=TypeError("ERR")) + self.assertRaises(TypeError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test13(self): + """13: if wait for pre_live_migration, but timeout. """ + rpc.call = dummyCall + + db.instance_get = Mock(return_value=self.instance1) + + result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + c1 = (None == result) + c2 = (0 <= sys.stderr.buffer.find('Timeout for')) + self.assertEqual(c1 and c2, True) + + def test14(self): + """14: if db_instance_get issues NotFound. + """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(side_effect=exception.NotFound("ERR")) + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test15(self): + """15: if rpc.call returns True, and instance_get() cause other + exception. (Unexpected case - b/c it already checked by + nova-manage) + """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(side_effect=TypeError("ERR")) + + self.assertRaises(TypeError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test16(self): + """16: if rpc.call returns True, and live_migration issues + ProcessExecutionError. """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(return_value=self.instance1) + ret = self.manager.driver.live_migration \ + = Mock(side_effect=utils.ProcessExecutionError("ERR")) + + self.assertRaises(utils.ProcessExecutionError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test17(self): + """17: everything goes well. """ + self.manager.driver.live_migration = Mock(return_value=True) + ret = self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + self.assertEqual(True, True) + + def tearDown(self): + """common terminating method. """ + self.stderr.realFlush() + sys.stderr = self.stderrBak + #sys.stdout = self.stdoutBak + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.DEBUG) + #unittest.main() + + suite = unittest.TestLoader().loadTestsFromTestCase(ComputeTestFunctions) + unittest.TextTestRunner(verbosity=2).run(suite) + + #suite = unittest.TestSuite() + #suite.addTest(ComputeTestFunctions("test15")) + #suite.addTest(ComputeTestFunctions("test16")) + #unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/nova/livemigration_test/UT/libvirtConnection.test.py b/nova/livemigration_test/UT/libvirtConnection.test.py new file mode 100644 index 000000000..6a353508d --- /dev/null +++ b/nova/livemigration_test/UT/libvirtConnection.test.py @@ -0,0 +1,366 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR='/opt/nova-2010.4' + +import sys +import unittest +import commands +import re +import logging +import libvirt + +from mock import Mock +import twisted + +try : + print + print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.compute.manager import ComputeManager + from nova.virt import libvirt_conn + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova import process + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def writelines(self, arg): + self.buffer += arg + def flush(self): + print 'flush' + self.buffer = '' + +class tmpStderr(tmpStdout): + def write(self,arg): + self.buffer += arg + def flush(self): + pass + def realFlush(self): + self.buffer = '' + +class DummyLibvirtConn(object): + nwfilterLookupByName = None + def __init__(self): + pass + + +class LibvirtConnectionTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + stderr = None + stderrBak = None + manager = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + #if self.stdout is None: + # self.__class__.stdout = tmpStdout() + #self.stdoutBak = sys.stdout + #sys.stdout = self.stdout + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + + self.host = 'openstack2-api' + if self.manager is None: + self.__class__.manager = libvirt_conn.get_connection(False) + + self.setTestData() + self.setMocks() + + def setTestData(self): + + self.host1 = Host() + for key, val in [ ('name', 'host1'), ('cpu', 5), ('memory_mb', 20480), ('hdd_gb', 876) ]: + self.host1.__setitem__(key, val) + + self.instance1 = Instance() + for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5), ('internal_id',12345) ]: + self.instance1.__setitem__(key, val) + + + self.instance2 = Instance() + for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance2.__setitem__(key, val) + + + self.fixed_ip1 = FixedIp() + for key, val in [ ('id', 1), ('address', '1.1.1.1'), ('network_id', '1'), + ('instance_id', 1)]: + self.fixed_ip1.__setitem__(key, val) + + self.floating_ip1 = FloatingIp() + for key, val in [ ('id', 1), ('address', '1.1.1.200') ]: + self.floating_ip1.__setitem__(key, val) + + self.netref1 = Network() + for key, val in [ ('id', 1) ]: + self.netref1.__setitem__(key, val) + + + def setMocks(self): + + self.ctxt = context.get_admin_context() + db.instance_get_fixed_address = Mock(return_value = '1.1.1.1') + db.fixed_ip_update = Mock(return_value = None) + db.fixed_ip_get_network = Mock(return_value = self.netref1) + db.network_update = Mock(return_value = None) + db.instance_get_floating_address = Mock(return_value = '1.1.1.200') + db.floating_ip_get_by_address = Mock(return_value = self.floating_ip1) + db.floating_ip_update = Mock(return_value = None) + db.instance_update = Mock(return_value = None) + + + # ---> test for nova.virt.libvirt_conn.nwfilter_for_instance_exists() + + def test01(self): + """01: libvirt.libvirtError occurs. """ + + self.manager._wrapped_conn = DummyLibvirtConn() + self.manager._test_connection = Mock(return_value=True) + self.manager._conn.nwfilterLookupByName = \ + Mock(side_effect=libvirt.libvirtError("ERR")) + ret = self.manager.nwfilter_for_instance_exists(self.instance1) + self.assertEqual(ret, False) + + def test02(self): + """02: libvirt.libvirtError not occurs. """ + + self.manager._wrapped_conn = DummyLibvirtConn() + self.manager._test_connection = Mock(return_value=True) + self.manager._conn.nwfilterLookupByName = \ + Mock(return_value=True) + ret = self.manager.nwfilter_for_instance_exists(self.instance1) + self.assertEqual(ret, True) + + # ---> test for nova.virt.libvirt_conn.live_migraiton() + + def test03(self): + """03: Unexpected exception occurs on finding volume on DB. """ + + utils.execute = Mock( side_effect=process.ProcessExecutionError('ERR') ) + + self.assertRaises(process.ProcessExecutionError, + self.manager.live_migration, + self.instance1, + 'host2') + + # ---> other case cannot be tested because live_migraiton + # is synchronized/asynchronized method are mixed together + + + # ---> test for nova.virt.libvirt_conn._post_live_migraiton + + def test04(self): + """04: instance_ref is not nova.db.sqlalchemy.models.Instances""" + + self.assertRaises(TypeError, + self.manager._post_live_migration, + "dummy string", + 'host2') + + def test05(self): + """05: db.instance_get_fixed_address return None""" + + db.instance_get_fixed_address = Mock( return_value=None ) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('fixed_ip is not found')) + self.assertEqual(c1 and c2, True) + + def test06(self): + """06: db.instance_get_fixed_address raises NotFound""" + + db.instance_get_fixed_address = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host2') + + def test07(self): + """07: db.instance_get_fixed_address raises Unknown exception""" + + db.instance_get_fixed_address = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test08(self): + """08: db.fixed_ip_update return NotFound. """ + + db.fixed_ip_update = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test09(self): + """09: db.fixed_ip_update return NotAuthorized. """ + db.fixed_ip_update = Mock( side_effect=exception.NotAuthorized('ERR') ) + self.assertRaises(exception.NotAuthorized, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test10(self): + """10: db.fixed_ip_update return Unknown exception. """ + db.fixed_ip_update = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test11(self): + """11: db.fixed_ip_get_network causes NotFound. """ + + db.fixed_ip_get_network = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host1') + + # not tested db.fixed_ip_get_network raises NotAuthorized + # because same test has been done at previous test. + + def test12(self): + """12: db.fixed_ip_get_network causes Unknown exception. """ + + db.fixed_ip_get_network = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test13(self): + """13: db.network_update raises Unknown exception. """ + db.network_update = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test14(self): + """14: db.instance_get_floating_address raises NotFound. """ + db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + + def test15(self): + """15: db.instance_get_floating_address returns None. """ + + db.instance_get_floating_address = Mock( return_value=None ) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('floating_ip is not found')) + self.assertEqual(c1 and c2, True) + + def test16(self): + """16: db.instance_get_floating_address raises NotFound. """ + + db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + def test17(self): + """17: db.instance_get_floating_address raises Unknown exception. """ + db.instance_get_floating_address = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + + def test18(self): + """18: db.floating_ip_get_by_address raises NotFound """ + + db.floating_ip_get_by_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + def test19(self): + """19: db.floating_ip_get_by_address raises Unknown exception. """ + db.floating_ip_get_by_address = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + + def test20(self): + """20: db.floating_ip_update raises Unknown exception. + """ + db.floating_ip_update = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + def test21(self): + """21: db.instance_update raises unknown exception. """ + + db.instance_update = Mock(side_effect=TypeError("ERR")) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def tearDown(self): + """common terminating method. """ + self.stderr.realFlush() + sys.stderr = self.stderrBak + #sys.stdout = self.stdoutBak + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.DEBUG) + #unittest.main() + + suite = unittest.TestLoader().loadTestsFromTestCase(LibvirtConnectionTestFunctions) + unittest.TextTestRunner(verbosity=2).run(suite) + + #suite = unittest.TestSuite() + #suite.addTest(LibvirtConnectionTestFunctions("test14")) + #suite.addTest(LibvirtConnectionTestFunctions("test16")) + #unittest.TextTestRunner(verbosity=2).run(suite) + + diff --git a/nova/livemigration_test/UT/nova-manage.test.py b/nova/livemigration_test/UT/nova-manage.test.py new file mode 100644 index 000000000..dabdba001 --- /dev/null +++ b/nova/livemigration_test/UT/nova-manage.test.py @@ -0,0 +1,318 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR='/opt/nova-2010.2' + +import sys +import unittest +import commands +import re + +from mock import Mock + +try : + print + print 'Testing %s/bin/nova-manage, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def flush(self): + self.buffer = '' + + +class NovaManageTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + + hostCmds = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + commands.getstatusoutput('cp -f %s/bin/nova-manage %s' % ( NOVA_DIR, self.getNovaManageCopyPath() )) + commands.getstatusoutput('touch %s' % self.getInitpyPath() ) + try : + import bin.novamanagetest + except: + print 'Fail to import nova-manage . check bin/nova-manage exists' + raise + + # replace stdout for checking nova-manage output + if self.stdout is None : + self.__class__.stdout = tmpStdout() + self.stdoutBak = sys.stdout + sys.stdout = self.stdout + + # prepare test data + self.setTestData() + + + def setTestData(self): + import bin.novamanagetest + + if self.hostCmds is None : + self.__class__.hostCmds = bin.novamanagetest.HostCommands() + self.instanceCmds = bin.novamanagetest.InstanceCommands() + + self.host1 = Host() + self.host1.__setitem__('name', 'host1') + + self.host2 = Host() + self.host2.__setitem__('name', 'host2') + + self.instance1 = Instance() + self.instance1.__setitem__('id', 1) + self.instance1.__setitem__('host', 'host1') + self.instance1.__setitem__('hostname', 'i-12345') + self.instance1.__setitem__('state', power_state.NOSTATE) + self.instance1.__setitem__('state_description', 'running') + + self.instance2 = Instance() + self.instance2.__setitem__('id', 2) + self.instance2.__setitem__('host', 'host1') + self.instance2.__setitem__('hostname', 'i-12345') + self.instance2.__setitem__('state', power_state.RUNNING) + self.instance2.__setitem__('state_description', 'pending') + + self.instance3 = Instance() + self.instance3.__setitem__('id', 3) + self.instance3.__setitem__('host', 'host1') + self.instance3.__setitem__('hostname', 'i-12345') + self.instance3.__setitem__('state', power_state.RUNNING) + self.instance3.__setitem__('state_description', 'running') + + db.host_get_all = Mock(return_value=[self.host1, self.host2]) + + def getInitpyPath(self): + return '%s/bin/__init__.py' % NOVA_DIR + + def getNovaManageCopyPath(self): + return '%s/bin/novamanagetest.py' % NOVA_DIR + + # -----> Test for nova-manage host list + + def test01(self): + """01: Got some host lists. """ + + self.hostCmds.list() + + c1 = (2 == self.stdout.buffer.count('\n')) + c2 = (0 <= self.stdout.buffer.find('host1')) + c3 = (0 <= self.stdout.buffer.find('host2')) + self.assertEqual(c1 and c2 and c3, True) + + def test02(self): + """02: Got empty lsit. """ + + db.host_get_all = Mock(return_value=[]) + self.hostCmds.list() + + # result should be empty + c = (0 == len(self.stdout.buffer) ) + self.assertEqual(c, True) + + def test03(self): + """03: Got notFound """ + + db.host_get_all = Mock(side_effect=exception.NotFound("ERR")) + self.assertRaises(exception.NotFound, self.hostCmds.list) + + # --------> Test For nova-manage host show + + def test04(self): + """04: args are not enough(nova-manage host show) """ + self.assertRaises(TypeError, self.hostCmds.show ) + + + def test05(self): + """05: nova-manage host show not-registered-host, and got an error""" + + rpc.call = Mock(return_value={'ret' : False, 'msg': 'ERR'} ) + self.hostCmds.show('host1') + self.assertEqual( self.stdout.buffer[:3]=='ERR', True ) + + + def test06(self): + """06: nova-manage host show registerd-host, and no project uses the host""" + + dic = {'ret': True, + 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, + 'usage': {}} + + rpc.call = Mock(return_value=dic ) + self.hostCmds.show('host1') + + # result should be : + # HOST PROJECT cpu mem(mb) disk(gb) + # host1 1 2 3 + line = self.stdout.buffer.split('\n')[1] + line = re.compile('\t+').sub(' ', line).strip() + c1 = ( 'host1 1 2 3' == line ) + c2 = ( self.stdout.buffer.count('\n') == 2 ) + + self.assertEqual( c1 and c2, True ) + + def test07(self): + """07: nova-manage host show registerd-host, + and some projects use the host + """ + dic = {'ret': True, + 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, + 'usage': {'p1': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, + 'p2': {'cpu':1, 'memory_mb':2, 'hdd_gb':3} }} + + rpc.call = Mock(return_value=dic ) + self.hostCmds.show('host1') + + # result should be : + # HOST PROJECT cpu mem(mb) disk(gb) + # host1 1 2 3 + # host1 p1 1 2 3 + # host1 p2 4 5 6 + line = self.stdout.buffer.split('\n')[1] + ret = re.compile('\t+').sub(' ', line).strip() + c1 = ( 'host1 1 2 3' == ret ) + + line = self.stdout.buffer.split('\n')[2] + line = re.compile('\t+').sub(' ', line).strip() + c2 = ( 'host1 p1 1 2 3' == line ) or ( 'host1 p2 1 2 3' == line ) + + line = self.stdout.buffer.split('\n')[3] + ret = re.compile('\t+').sub(' ', line).strip() + c3 = ( 'host1 p1 1 2 3' == ret ) or ( 'host1 p2 1 2 3' == ret ) + + self.assertEqual( c1 and c2 and c3, True ) + + def test08(self): + """08: nova-manage host show registerd-host, and rpc.call returns None + (unexpected error) + """ + rpc.call = Mock(return_value=None ) + self.hostCmds.show('host1') + c1 = ( 0 <= self.stdout.buffer.find('Unexpected error') ) + self.assertEqual( c1, True ) + + # ----------> Test for bin/nova-manage instance live_migration + + def test09(self): + """09: arguments are not enough(nova-manage instances live_migration) + """ + self.assertRaises(TypeError, self.instanceCmds.live_migration ) + + def test10(self): + """10: arguments are not enough(nova-manage instances live_migration ec2_id) + """ + self.assertRaises(TypeError, self.instanceCmds.live_migration, 'i-xxx' ) + + def test11(self): + """11: nova-manage instances live_migration ec2_id host, + where hostname is invalid + """ + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) + + def test12(self): + """12: nova-manage instances live_migration ec2_id(invalid id) host""" + + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( side_effect=exception.NotFound('ERR') ) + + self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) + + def test13(self): + """13: nova-manage instances live_migration ec2_id host, + but instance specifed by ec2 id is not running (state is not power_state.RUNNING) + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) + c1 = c2 = False + try : + self.instanceCmds.live_migration('i-12345', 'host1') + except SystemExit, e: + c1 = (1 == e.code) + c2 = (0 < self.stdout.buffer.find('is not running') ) + self.assertEqual( c1 and c2 , True ) + + + def test14(self): + """14: nova-manage instances live_migration ec2_id host, + but instance specifed by ec2 id is not running (state_description is not running) + """ + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) + c1 = c2 = False + try : + self.instanceCmds.live_migration('i-12345', 'host2') + except SystemExit, e: + c1 = (1 == e.code) + c2 = (0 < self.stdout.buffer.find('is not running') ) + self.assertEqual( c1 and c2 , True ) + + def test15(self): + """15: nova-manage instances live_migration ec2_id host, + but instance is running at the same host specifed above, so err should be occured. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + c1 = c2 = False + try : + self.instanceCmds.live_migration('i-12345', 'host1') + except SystemExit, e: + c1 = (2 == e.code) + c2 = (0 < self.stdout.buffer.find('is running now') ) + self.assertEqual( c1 and c2 , True ) + + def test16(self): + """16: nova-manage instances live_migration ec2_id host, + everything goes well, ang gets success messages. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + rpc.cast = Mock(return_value = None) + + self.instanceCmds.live_migration('i-12345', 'host2') + c1 = (0 <= self.stdout.buffer.find('Finished all procedure') ) + self.assertEqual( c1, True ) + + + def tearDown(self): + """common terminating method. """ + commands.getstatusoutput('rm -rf %s' % self.getInitpyPath() ) + commands.getstatusoutput('rm -rf %s' % self.getNovaManageCopyPath() ) + sys.stdout.flush() + sys.stdout = self.stdoutBak + +if __name__ == '__main__': + #unittest.main() + suite = unittest.TestLoader().loadTestsFromTestCase(NovaManageTestFunctions) + unittest.TextTestRunner(verbosity=3).run(suite) + + diff --git a/nova/livemigration_test/UT/schedulerManager.test.py b/nova/livemigration_test/UT/schedulerManager.test.py new file mode 100644 index 000000000..2fe4d0994 --- /dev/null +++ b/nova/livemigration_test/UT/schedulerManager.test.py @@ -0,0 +1,360 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR='/opt/nova-2010.2' + +import sys +import unittest +import commands +import re + +from mock import Mock + +try : + print + print 'Checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.scheduler.manager import SchedulerManager + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def flush(self): + self.buffer = '' + + +class SchedulerTestFunctions(unittest.TestCase): + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + self.host = 'openstack2-api' + self.manager = SchedulerManager(host=self.host) + + self.setTestData() + + def setTestData(self): + + self.host1 = Host() + self.host1.__setitem__('name', 'host1') + self.host1.__setitem__('cpu', 5) + self.host1.__setitem__('memory_mb', 20480) + self.host1.__setitem__('hdd_gb', 876) + + self.host2 = Host() + self.host2.__setitem__('name', 'host2') + self.host2.__setitem__('cpu', 5) + self.host2.__setitem__('memory_mb', 20480) + self.host2.__setitem__('hdd_gb', 876) + + self.instance1 = Instance() + for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance1.__setitem__(key, val) + + + self.instance2 = Instance() + for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance2.__setitem__(key, val) + + + self.instance3 = Instance() + for key, val in [ ('id', 3), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance3.__setitem__(key, val) + + self.instance4 = Instance() + for key, val in [ ('id', 4), ('host', 'host2'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance4.__setitem__(key, val) + + self.instance5 = Instance() + for key, val in [ ('id', 5), ('host', 'host2'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance5.__setitem__(key, val) + + self.instance6 = Instance() + for key, val in [ ('id', 6), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance6.__setitem__(key, val) + + self.instance7 = Instance() + for key, val in [ ('id', 7), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 18432), ('local_gb', 5) ]: + self.instance7.__setitem__(key, val) + + self.instance8 = Instance() + for key, val in [ ('id', 8), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 866) ]: + self.instance8.__setitem__(key, val) + + + + def check_format(self, val): + """check result format of show_host_resource """ + + if dict != type(val) : + sys.stderr.write('return value is not dict') + return False + + if not val.has_key('ret'): + sys.stderr.write('invalid format(missing "ret"). ') + return False + + if not val['ret'] : + if not val.has_key('msg') : + sys.stderr.write( 'invalid format(missing "msg").' ) + return False + + else : + if not val.has_key('phy_resource') : + sys.stderr.write('invalid format(missing "phy_resource"). ') + return False + + if not val.has_key('usage'): + sys.stderr.write('invalid format(missing "usage"). ') + return False + + if not self._check_format(val['phy_resource']): + return False + + for key, dic in val['usage'].items() : + if not self._check_format(dic): + return False + return True + + def _check_format(self, val): + if dict != type(val) : + sys.stderr.write('return value is not dict') + return False + + for key in ['cpu', 'memory_mb', 'hdd_gb']: + if not val.has_key(key) : + sys.stderr.write('invalid format(missing "%s"). ' % key ) + return False + + return True + + # ---> test for nova.scheduler.manager.show_host_resource() + + def test01(self): + """01: get NotFound exception when dest host not found on DB """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + result = self.manager.show_host_resource(ctxt, 'not-registered-host') + c1 = ( not result['ret'] ) + c2 = ( 0 == result['msg'].find('No such') ) + self.assertEqual(c1 and c2, True) + + def test02(self): + """02: get other exception if unexpected err. """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, self.manager.show_host_resource, ctxt, 'host1' ) + + def test03(self): + """03: no instance found on dest host. """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( return_value = self.host1 ) + db.instance_get_all_by_host = Mock( return_value=[]) + ret= self.manager.show_host_resource(ctxt, 'host1') + + c1 = self.check_format(ret) + v = ret['phy_resource'] + c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) + c3 = ( 0 == len(ret['usage']) ) + + self.assertEqual(c1 and c2 and c3, True) + + def test04(self): + """04: some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( return_value = self.host1 ) + db.instance_get_all_by_host = Mock( return_value=[ self.instance1, + self.instance2, + self.instance3] ) + + db.instance_get_vcpu_sum_by_host_and_project = Mock(return_value=3) + db.instance_get_memory_sum_by_host_and_project = Mock(return_value=1024) + db.instance_get_disk_sum_by_host_and_project = Mock(return_value=5) + + ret= self.manager.show_host_resource(ctxt, 'host1') + + c1 = self.check_format(ret) + v = ret['phy_resource'] + c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) + c3 = ( 2 == len(ret['usage']) ) + c4 = ( self.instance1['project_id'] in ret['usage'].keys()) + c5 = ( self.instance3['project_id'] in ret['usage'].keys()) + + self.assertEqual(c1 and c2 and c3 and c4 and c5, True) + + + # ---> test for nova.scheduler.manager.has_enough_resource() + def test05(self): + """05: when cpu is exccded some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance6) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + def test06(self): + """06: when memory is exccded some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance7) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + def test07(self): + """07: when hdd is exccded some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance8) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + def test08(self): + """08: everything goes well. (instance_get_all_by_host returns list)""" + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, True) + + + def test09(self): + """09: everything goes well(instance_get_all_by_host returns[]). """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, True) + + + # ---> test for nova.scheduler.manager.live_migration() + + + def test10(self): + """10: instance_get_by_internal_id issue NotFound. """ + # Mocks for has_enough_resource() + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance8) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + # Mocks for live_migration()db.instance_get_by_internal_id + # (any Mock is ok here. important mock is all above) + db.instance_get_by_internal_id = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.live_migration, + ctxt, + 'i-12345', + 'host1') + + + def test11(self): + """11: return False if host doesnt have enough resource. """ + + # Mocks for has_enough_resource() + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance8) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + # Mocks for live_migration()db.instance_get_by_internal_id + # (any Mock is ok here. important mock is all above) + db.instance_get_by_internal_id = Mock(return_value = self.instance8) + db.instance_set_state = Mock(return_value = True) + rpc_cast = Mock(return_value = True) + + ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + + + def test12(self): + """12: everything goes well. """ + + # Mocks for has_enough_resource() + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + # Mocks for live_migration()db.instance_get_by_internal_id + # (any Mock is ok here. important mock is all above) + db.instance_get_by_internal_id = Mock(return_value = self.instance8) + db.instance_set_state = Mock(return_value = True) + rpc.cast = Mock(return_value = True) + + ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, True) + + + def tearDown(self): + """common terminating method. """ + #sys.stdout = self.stdoutBak + pass + +if __name__ == '__main__': + #unittest.main() + suite = unittest.TestLoader().loadTestsFromTestCase(SchedulerTestFunctions) + unittest.TextTestRunner(verbosity=3).run(suite) + + diff --git a/nova/livemigration_test/UT/testCase_UT.xls b/nova/livemigration_test/UT/testCase_UT.xls new file mode 100644 index 000000000..0524526b6 Binary files /dev/null and b/nova/livemigration_test/UT/testCase_UT.xls differ -- cgit From df045f9252f6a50171d477c265564f062294e47a Mon Sep 17 00:00:00 2001 From: masumotok Date: Mon, 20 Dec 2010 08:06:11 +0900 Subject: テストコードをレポジトリに追加 nova.compute.manager.pre_live_migration()について、異常終了しているのに正常終了の戻り値を返すことがあったため変更 - 正常終了の戻り値をTrueに変更 - fixed_ipが見つからないときにはRemoteErrorをraiseする - それに合わせてnova.compute.manager.live_migrationも変更 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/compute/manager.py | 13 +- nova/livemigration_test/SI/picture.pptx | Bin 0 -> 137730 bytes nova/livemigration_test/SI/testCase_SI.xls | Bin 0 -> 35840 bytes .../SI/testParameterSheet_SI.xls | Bin 0 -> 464384 bytes nova/livemigration_test/SI/utils/demo-firstboot.sh | 39 ++ .../SI/utils/demo-runInstance.sh | 57 +++ nova/livemigration_test/SI/utils/nova-manage.conf | 18 + nova/livemigration_test/SI/utils/nova.conf | 10 + nova/livemigration_test/SI/utils/nova.sh | 180 +++++++++ nova/livemigration_test/SI/utils/nova.sh.compute | 37 ++ nova/livemigration_test/UT/computeManager.test.py | 407 +++++++++++++++++++++ .../UT/libvirtConnection.test.py | 366 ++++++++++++++++++ nova/livemigration_test/UT/nova-manage.test.py | 318 ++++++++++++++++ .../livemigration_test/UT/schedulerManager.test.py | 360 ++++++++++++++++++ nova/livemigration_test/UT/testCase_UT.xls | Bin 0 -> 195072 bytes 15 files changed, 1799 insertions(+), 6 deletions(-) create mode 100644 nova/livemigration_test/SI/picture.pptx create mode 100644 nova/livemigration_test/SI/testCase_SI.xls create mode 100644 nova/livemigration_test/SI/testParameterSheet_SI.xls create mode 100755 nova/livemigration_test/SI/utils/demo-firstboot.sh create mode 100755 nova/livemigration_test/SI/utils/demo-runInstance.sh create mode 100644 nova/livemigration_test/SI/utils/nova-manage.conf create mode 100644 nova/livemigration_test/SI/utils/nova.conf create mode 100755 nova/livemigration_test/SI/utils/nova.sh create mode 100755 nova/livemigration_test/SI/utils/nova.sh.compute create mode 100644 nova/livemigration_test/UT/computeManager.test.py create mode 100644 nova/livemigration_test/UT/libvirtConnection.test.py create mode 100644 nova/livemigration_test/UT/nova-manage.test.py create mode 100644 nova/livemigration_test/UT/schedulerManager.test.py create mode 100644 nova/livemigration_test/UT/testCase_UT.xls (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 81cca7770..bad525115 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -39,6 +39,7 @@ import logging import sys import traceback import os +import time from twisted.internet import defer @@ -297,10 +298,10 @@ class ComputeManager(manager.Manager): # 2. getting fixed ips fixed_ip = db.instance_get_fixed_address(context, instance_id) if None == fixed_ip: - logging.error('Not found fixedip for %s\n%s', - ec2_id, - ''.join(traceback.format_tb(sys.exc_info()[2]))) - return + exc_type = 'NotFoundError' + val = '%s(%s) doesnt have fixed_ip ' % (instance_id, ec2_id) + tb = ''.join(traceback.format_tb(sys.exc_info()[2])) + raise rpc.RemoteError(exc_type, val, tb) # 3. if any volume is mounted, prepare here. if 0 != len(shelf_slots): @@ -315,6 +316,7 @@ class ComputeManager(manager.Manager): # 5. bridge settings self.network_manager.setup_compute_network(instance_id) + return True def nwfilter_for_instance_exists(self, context, instance_id): """Check nova-instance-instance-xxx filter exists """ @@ -324,7 +326,6 @@ class ComputeManager(manager.Manager): def live_migration(self, context, instance_id, dest): """executes live migration.""" - import time # 1. ask dest host to preparing live migration. compute_topic = db.queue_get_for(context, FLAGS.compute_topic, dest) ret = rpc.call(context, @@ -333,7 +334,7 @@ class ComputeManager(manager.Manager): "args": {'instance_id': instance_id, 'dest': dest}}) - if rpc.RemoteError == type(ret): + if True != ret: logging.error('Live migration failed(err at %s)', dest) db.instance_set_state(context, instance_id, diff --git a/nova/livemigration_test/SI/picture.pptx b/nova/livemigration_test/SI/picture.pptx new file mode 100644 index 000000000..b47bec9b5 Binary files /dev/null and b/nova/livemigration_test/SI/picture.pptx differ diff --git a/nova/livemigration_test/SI/testCase_SI.xls b/nova/livemigration_test/SI/testCase_SI.xls new file mode 100644 index 000000000..723363c1e Binary files /dev/null and b/nova/livemigration_test/SI/testCase_SI.xls differ diff --git a/nova/livemigration_test/SI/testParameterSheet_SI.xls b/nova/livemigration_test/SI/testParameterSheet_SI.xls new file mode 100644 index 000000000..192d9705b Binary files /dev/null and b/nova/livemigration_test/SI/testParameterSheet_SI.xls differ diff --git a/nova/livemigration_test/SI/utils/demo-firstboot.sh b/nova/livemigration_test/SI/utils/demo-firstboot.sh new file mode 100755 index 000000000..3a6f7fb0b --- /dev/null +++ b/nova/livemigration_test/SI/utils/demo-firstboot.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +DIR=/opt/nova-2010.1 + +# 1. 管理者ユーザを作成する +# nova-manage user admin ユーザ名 access-key secret-key +# +#$DIR/bin/nova-manage user admin admin admin admin + +# 2. プロジェクトを作成する +# nova-manage create project プロジェクト名 プロジェクトに属するユーザ名 +# +#$DIR/bin/nova-manage project create admin admin + +# 3. クラウドを使うための認証情報を生成する +# nova-manage project environment プロジェクト名 ユーザ名 認証情報を格納するファイル +# +#$DIR/bin/nova-manage project environment admin admin $DIR/novarc + +# 4. 認証情報の読み込み +. $DIR/novarc + +# 5. プロジェクト用仮想マシンネットワークの作成を行う +# nova-manage user admin ユーザ名 access-key secret-key +# +$DIR/bin/nova-manage network create 10.0.0.0/8 3 16 + +# 6. 初回ログインにはSSHの公開鍵認証が必要 +# +if [ "" == "`euca-describe-keypairs | grep testkey`" ]; then + euca-add-keypair testkey > testkey.pem +fi + +# 7. +for i in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do + sudo ip addr del $i dev eth0 2> /dev/null +done + + diff --git a/nova/livemigration_test/SI/utils/demo-runInstance.sh b/nova/livemigration_test/SI/utils/demo-runInstance.sh new file mode 100755 index 000000000..171291262 --- /dev/null +++ b/nova/livemigration_test/SI/utils/demo-runInstance.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +DIR=/opt/nova-2010.1 + +function inc_assigned(){ + assigned=`expr $assigned + 1` +} + + +# 1. 認証情報の読み込み +. $DIR/novarc + +# 3. 仮想マシンの起動 +# +ret=`euca-run-instances -t m1.small -k testkey ami-centos` +#ret=`euca-run-instances -t m1.small -k testkey ami-tiny` + +# 4. 仮想マシン用IPの確保 +# 未登録なら登録しておく +registered=`euca-describe-addresses` +for ip in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do + + not_registered=`echo $registered | grep $ip` + if [ "" == "$not_registered" ]; then + echo "[INFO] registed $ip" + $DIR/bin/nova-manage floating create `hostname` $ip + fi +done + +# 5. IPの割当 +echo 0 > /tmp/demo-runinstance +euca-describe-addresses | grep -v reserved | while read line; do + # 割り当てられてないものを仮想マシンに割り当てる + ip=`echo $line | cut -d ' ' -f 2` + id=`echo $ret | cut -d ' ' -f 5` + if [ "" == "`echo $id | grep i- `" ] ; then + echo "[INFO] try again" $ret + break + fi + echo "[INFO] assigned to ipaddr($ip) to instance($id) " + euca-associate-address -i $id $ip + echo 1 > /tmp/demo-runinstance + break +done + +echo $assigned +if [ 0 -eq "`cat /tmp/demo-runinstance`" ] ; then + echo "[INFO] address is full." +fi +rm -rf /tmp/demo-runinstance + + +# 6. FWの設定 +euca-authorize -P tcp -p 22 default 2> /dev/null > /dev/null +euca-authorize -P tcp -p 80 default 2> /dev/null > /dev/null +euca-authorize -P tcp -p 5555 default 2> /dev/null > /dev/null + diff --git a/nova/livemigration_test/SI/utils/nova-manage.conf b/nova/livemigration_test/SI/utils/nova-manage.conf new file mode 100644 index 000000000..9f8a02b96 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova-manage.conf @@ -0,0 +1,18 @@ +--verbose +--nodaemon +--dhcpbridge_flagfile=/etc/nova/nova-manage.conf +--FAKE_subdomain=ec2 +--libvirt_type=qemu +--no_internet_conn=True +--public_netif=eth0 +--public_interface=eth0 + +--cc-host=172.19.0.131 +--routing_source_ip=172.19.0.131 +--sql_connection=mysql://root:nova@172.19.0.131/nova +--rabbit_host=172.19.0.131 +--redis_host=172.19.0.131 +--s3_host=172.19.0.131 +--auth_driver=nova.auth.ldapdriver.LdapDriver +--ldap_url=ldap://172.19.0.131 + diff --git a/nova/livemigration_test/SI/utils/nova.conf b/nova/livemigration_test/SI/utils/nova.conf new file mode 100644 index 000000000..c66bfbc53 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova.conf @@ -0,0 +1,10 @@ +--verbose +--nodaemon +--dhcpbridge_flagfile=/opt/nova-2010.4//bin/nova.conf +--network_manager=nova.network.manager.VlanManager +--cc_host=172.19.0.131 +--routing_source_ip=172.19.0.131 +--sql_connection=mysql://root:nova@localhost/nova +--auth_driver=nova.auth.ldapdriver.LdapDriver +--libvirt_type=qemu +--public_interface=eth0 diff --git a/nova/livemigration_test/SI/utils/nova.sh b/nova/livemigration_test/SI/utils/nova.sh new file mode 100755 index 000000000..b8e2e9f26 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +DIR=`pwd` +CMD=$1 +SOURCE_BRANCH=lp:nova +if [ -n "$2" ]; then + SOURCE_BRANCH=$2 +fi +#DIRNAME=nova +DIRNAME="" +NOVA_DIR=$DIR/$DIRNAME +if [ -n "$3" ]; then + NOVA_DIR=$DIR/$3 +fi + +if [ ! -n "$HOST_IP" ]; then + # NOTE(vish): This will just get the first ip in the list, so if you + # have more than one eth device set up, this will fail, and + # you should explicitly set HOST_IP in your environment + HOST_IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` +fi + +#USE_MYSQL=${USE_MYSQL:-0} +USE_MYSQL=1 +MYSQL_PASS=${MYSQL_PASS:-nova} +TEST=${TEST:-0} +#USE_LDAP=${USE_LDAP:-0} +USE_LDAP=1 +LIBVIRT_TYPE=${LIBVIRT_TYPE:-qemu} +NET_MAN=${NET_MAN:-VlanManager} +# NOTE(vish): If you are using FlatDHCP on multiple hosts, set the interface +# below but make sure that the interface doesn't already have an +# ip or you risk breaking things. +# FLAT_INTERFACE=eth0 + +if [ "$USE_MYSQL" == 1 ]; then + SQL_CONN=mysql://root:$MYSQL_PASS@localhost/nova +else + SQL_CONN=sqlite:///$NOVA_DIR/nova.sqlite +fi + +if [ "$USE_LDAP" == 1 ]; then + AUTH=ldapdriver.LdapDriver +else + AUTH=dbdriver.DbDriver +fi + +mkdir -p /etc/nova +cat >$NOVA_DIR/bin/nova.conf << NOVA_CONF_EOF +--verbose +--nodaemon +--dhcpbridge_flagfile=$NOVA_DIR/bin/nova.conf +--network_manager=nova.network.manager.$NET_MAN +--cc_host=$HOST_IP +--routing_source_ip=$HOST_IP +--sql_connection=$SQL_CONN +--auth_driver=nova.auth.$AUTH +--libvirt_type=$LIBVIRT_TYPE +--public_interface=eth0 +NOVA_CONF_EOF + +if [ -n "$FLAT_INTERFACE" ]; then + echo "--flat_interface=$FLAT_INTERFACE" >>$NOVA_DIR/bin/nova.conf +fi + +if [ "$CMD" == "branch" ]; then + sudo apt-get install -y bzr + rm -rf $NOVA_DIR + bzr branch $SOURCE_BRANCH $NOVA_DIR + cd $NOVA_DIR + mkdir -p $NOVA_DIR/instances + mkdir -p $NOVA_DIR/networks +fi + +# You should only have to run this once +if [ "$CMD" == "install" ]; then + sudo apt-get install -y python-software-properties + sudo add-apt-repository ppa:nova-core/ppa + sudo apt-get update + sudo apt-get install -y dnsmasq kpartx kvm gawk iptables ebtables + sudo apt-get install -y user-mode-linux kvm libvirt-bin + sudo apt-get install -y screen euca2ools vlan curl rabbitmq-server + sudo apt-get install -y lvm2 iscsitarget open-iscsi + echo "ISCSITARGET_ENABLE=true" | sudo tee /etc/default/iscsitarget + sudo /etc/init.d/iscsitarget restart + sudo modprobe kvm + sudo /etc/init.d/libvirt-bin restart + sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot + sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy + sudo apt-get install -y python-libvirt python-libxml2 python-routes + if [ "$USE_MYSQL" == 1 ]; then + cat </etc/nova/nova-manage.conf << NOVA_CONF_EOF +--verbose +--nodaemon +--dhcpbridge_flagfile=/etc/nova/nova-manage.conf +--FAKE_subdomain=ec2 +--libvirt_type=qemu +--no_internet_conn=True +--public_netif=eth0 +--public_interface=eth0 + +--cc-host=$HOST_IP +--routing_source_ip=$HOST_IP +--sql_connection=mysql://root:nova@$HOST_IP/nova +--rabbit_host=$HOST_IP +--redis_host=$HOST_IP +--s3_host=$HOST_IP +--auth_driver=nova.auth.ldapdriver.LdapDriver +--ldap_url=ldap://$HOST_IP + +NOVA_CONF_EOF + +$DIR/bin/nova-compute --flagfile=/etc/nova/nova-manage.conf + diff --git a/nova/livemigration_test/UT/computeManager.test.py b/nova/livemigration_test/UT/computeManager.test.py new file mode 100644 index 000000000..d28d3ccb6 --- /dev/null +++ b/nova/livemigration_test/UT/computeManager.test.py @@ -0,0 +1,407 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR = '/opt/openstack/nova' +#NOVA_DIR = '/opt/nova-2010.4' + +import sys +import unittest +import commands +import re +import logging + +from mock import Mock +import twisted + +try: + print + print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' \ + % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.compute.manager import ComputeManager + from nova.virt.libvirt_conn import LibvirtConnection + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + + def write(self, arg): + self.buffer += arg + + def writelines(self, arg): + self.buffer += arg + + def flush(self): + print 'flush' + self.buffer = '' + + +class tmpStderr(tmpStdout): + def write(self, arg): + self.buffer += arg + + def flush(self): + pass + + def realFlush(self): + self.buffer = '' + +dummyCallReturnValue={ 0:True } +dummyCallCount=0 +def dummyCall(context, topic, method): + global dummyCallReturnValue, dummyCallCount + if dummyCallCount in dummyCallReturnValue.keys() : + ret = dummyCallReturnValue[ dummyCallCount ] + dummyCallCount += 1 + return ret + else : + dummyCallCount += 1 + return False + + +class ComputeTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + stderr = None + stderrBak = None + manager = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + #if self.stdout is None: + # self.__class__.stdout = tmpStdout() + #self.stdoutBak = sys.stdout + #sys.stdout = self.stdout + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + + self.host = 'openstack2-api' + if self.manager is None: + self.__class__.manager = ComputeManager(host=self.host) + + self.setTestData() + self.setMocks() + + def setTestData(self): + + self.host1 = Host() + for key, val in [('name', 'host1'), ('cpu', 5), + ('memory_mb', 20480), ('hdd_gb', 876)]: + self.host1.__setitem__(key, val) + + self.host2 = Host() + for key, val in [('name', 'host2'), ('cpu', 5), + ('memory_mb', 20480), ('hdd_gb', 876)]: + self.host2.__setitem__(key, val) + + self.instance1 = Instance() + for key, val in [('id', 1), ('host', 'host1'), + ('hostname', 'i-12345'), ('state', power_state.RUNNING), + ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), + ('hdd_gb', 5), ('internal_id', 12345)]: + self.instance1.__setitem__(key, val) + + self.instance2 = Instance() + for key, val in [('id', 2), ('host', 'host1'), + ('hostname', 'i-12345'), ('state', power_state.RUNNING), + ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), + ('hdd_gb', 5)]: + self.instance2.__setitem__(key, val) + + self.fixed_ip1 = FixedIp() + for key, val in [('id', 1), ('address', '1.1.1.1'), + ('network_id', '1'), ('instance_id', 1)]: + self.fixed_ip1.__setitem__(key, val) + + self.vol1 = Volume() + for key, val in [('id', 1), ('ec2_id', 'vol-qijjuc7e'), + ('availability_zone', 'nova'), ('host', 'host1')]: + self.vol1.__setitem__(key, val) + + self.vol2 = Volume() + for key, val in [('id', 2), ('ec2_id', 'vol-qi22222'), + ('availability_zone', 'nova'), ('host', 'host1')]: + self.vol2.__setitem__(key, val) + + self.secgrp1 = Volume() + for key, val in [('id', 1), ('ec2_id', 'default')]: + self.secgrp1.__setitem__(key, val) + + self.secgrp2 = Volume() + for key, val in [('id', 2), ('ec2_id', 'def2')]: + self.secgrp2.__setitem__(key, val) + + self.netref1 = Network() + + def setMocks(self): + + # mocks for pre_live_migration + self.ctxt = context.get_admin_context() + db.instance_get = Mock(return_value=self.instance1) + db.volume_get_by_ec2_id = Mock(return_value=[self.vol1, self.vol2]) + db.volume_get_shelf_and_blade = Mock(return_value=(3, 4)) + db.instance_get_fixed_address = Mock(return_value=self.fixed_ip1) + db.security_group_get_by_instance \ + = Mock(return_value=[self.secgrp1, self.secgrp2]) + self.manager.driver.setup_nwfilters_for_instance \ + = Mock(return_value=None) + self.manager.driver.nwfilter_for_instance_exists = Mock(return_value=None) + self.manager.network_manager.setup_compute_network \ + = Mock(return_value=None) + # mocks for live_migration_ + rpc.call = Mock(return_value=True) + db.instance_set_state = Mock(return_value=True) + + # ---> test for nova.compute.manager.pre_live_migration() + def test01(self): + """01: NotFound error occurs on finding instance on DB. """ + + db.instance_get = Mock(side_effect=exception.NotFound('ERR')) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test02(self): + """02: NotAuthrized occurs on finding volume on DB. """ + + db.volume_get_by_ec2_id \ + = Mock(side_effect=exception.NotAuthorized('ERR')) + + self.assertRaises(exception.NotAuthorized, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test03(self): + """03: Unexpected exception occurs on finding volume on DB. """ + + db.volume_get_by_ec2_id = Mock(side_effect=TypeError('ERR')) + + self.assertRaises(TypeError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test04(self): + """04: no volume and fixed ip found on DB, """ + + db.volume_get_by_ec2_id = Mock(side_effect=exception.NotFound('ERR')) + db.instance_get_fixed_address = Mock(return_value=None) + + self.assertRaises(rpc.RemoteError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + c1 = (0 <= sys.stderr.buffer.find('has no volume')) + + self.assertEqual(c1, True) + + def test05(self): + """05: volume found and no fixed_ip found on DB. """ + + db.instance_get_fixed_address \ + = Mock(side_effect=exception.NotFound('ERR')) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test06(self): + """06: self.driver.setup_nwfilters_for_instance causes NotFound. """ + self.manager.driver.setup_nwfilters_for_instance \ + = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test07(self): + """07: self.network_manager.setup_compute_network causes ProcessExecutionError. """ + self.manager.network_manager.setup_compute_network \ + = Mock(side_effect=exception.ProcessExecutionError("ERR")) + + self.assertRaises(exception.ProcessExecutionError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + + def test08(self): + """08: self.manager.network_manager.setup_compute_network + exception.NotFound. """ + self.manager.network_manager.setup_compute_network \ + = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + # those 2 cases are omitted : + # self.driver.setup_nwfilters_for_instance causes + # twisted.python.failure.Failure. + # self.driver.refresh_security_group causes twisted.python.failure.Failure. + # + # twisted.python.failure.Failure can not be used with assertRaises, + # it doesnt have __call___ + # + + def test09(self): + """09: volume/fixed_ip found on DB, all procedure finish + successfully.. """ + + result = self.manager.pre_live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + self.assertEqual(result, True) + + # ---> test for nova.compute.manager.live_migration() + + def test10(self): + """10: rpc.call(pre_live_migration returns Error(Not None). """ + rpc.call = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test11(self): + """11: if rpc.call returns rpc.RemoteError. """ + + rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) + db.instance_set_state = Mock(return_value=True) + result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + c1 = (None == result) + c2 = (0 <= sys.stderr.buffer.find('err at')) + self.assertEqual(c1 and c2, True) + + def test12(self): + """12: if rpc.call returns rpc.RemoteError and instance_set_state + also ends up err. (then , unexpected err occurs, in this case + TypeError) + """ + rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) + db.instance_set_state = Mock(side_effect=TypeError("ERR")) + self.assertRaises(TypeError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test13(self): + """13: if wait for pre_live_migration, but timeout. """ + rpc.call = dummyCall + + db.instance_get = Mock(return_value=self.instance1) + + result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + c1 = (None == result) + c2 = (0 <= sys.stderr.buffer.find('Timeout for')) + self.assertEqual(c1 and c2, True) + + def test14(self): + """14: if db_instance_get issues NotFound. + """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(side_effect=exception.NotFound("ERR")) + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test15(self): + """15: if rpc.call returns True, and instance_get() cause other + exception. (Unexpected case - b/c it already checked by + nova-manage) + """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(side_effect=TypeError("ERR")) + + self.assertRaises(TypeError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test16(self): + """16: if rpc.call returns True, and live_migration issues + ProcessExecutionError. """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(return_value=self.instance1) + ret = self.manager.driver.live_migration \ + = Mock(side_effect=utils.ProcessExecutionError("ERR")) + + self.assertRaises(utils.ProcessExecutionError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test17(self): + """17: everything goes well. """ + self.manager.driver.live_migration = Mock(return_value=True) + ret = self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + self.assertEqual(True, True) + + def tearDown(self): + """common terminating method. """ + self.stderr.realFlush() + sys.stderr = self.stderrBak + #sys.stdout = self.stdoutBak + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.DEBUG) + #unittest.main() + + suite = unittest.TestLoader().loadTestsFromTestCase(ComputeTestFunctions) + unittest.TextTestRunner(verbosity=2).run(suite) + + #suite = unittest.TestSuite() + #suite.addTest(ComputeTestFunctions("test15")) + #suite.addTest(ComputeTestFunctions("test16")) + #unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/nova/livemigration_test/UT/libvirtConnection.test.py b/nova/livemigration_test/UT/libvirtConnection.test.py new file mode 100644 index 000000000..6a353508d --- /dev/null +++ b/nova/livemigration_test/UT/libvirtConnection.test.py @@ -0,0 +1,366 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR='/opt/nova-2010.4' + +import sys +import unittest +import commands +import re +import logging +import libvirt + +from mock import Mock +import twisted + +try : + print + print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.compute.manager import ComputeManager + from nova.virt import libvirt_conn + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova import process + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def writelines(self, arg): + self.buffer += arg + def flush(self): + print 'flush' + self.buffer = '' + +class tmpStderr(tmpStdout): + def write(self,arg): + self.buffer += arg + def flush(self): + pass + def realFlush(self): + self.buffer = '' + +class DummyLibvirtConn(object): + nwfilterLookupByName = None + def __init__(self): + pass + + +class LibvirtConnectionTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + stderr = None + stderrBak = None + manager = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + #if self.stdout is None: + # self.__class__.stdout = tmpStdout() + #self.stdoutBak = sys.stdout + #sys.stdout = self.stdout + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + + self.host = 'openstack2-api' + if self.manager is None: + self.__class__.manager = libvirt_conn.get_connection(False) + + self.setTestData() + self.setMocks() + + def setTestData(self): + + self.host1 = Host() + for key, val in [ ('name', 'host1'), ('cpu', 5), ('memory_mb', 20480), ('hdd_gb', 876) ]: + self.host1.__setitem__(key, val) + + self.instance1 = Instance() + for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5), ('internal_id',12345) ]: + self.instance1.__setitem__(key, val) + + + self.instance2 = Instance() + for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance2.__setitem__(key, val) + + + self.fixed_ip1 = FixedIp() + for key, val in [ ('id', 1), ('address', '1.1.1.1'), ('network_id', '1'), + ('instance_id', 1)]: + self.fixed_ip1.__setitem__(key, val) + + self.floating_ip1 = FloatingIp() + for key, val in [ ('id', 1), ('address', '1.1.1.200') ]: + self.floating_ip1.__setitem__(key, val) + + self.netref1 = Network() + for key, val in [ ('id', 1) ]: + self.netref1.__setitem__(key, val) + + + def setMocks(self): + + self.ctxt = context.get_admin_context() + db.instance_get_fixed_address = Mock(return_value = '1.1.1.1') + db.fixed_ip_update = Mock(return_value = None) + db.fixed_ip_get_network = Mock(return_value = self.netref1) + db.network_update = Mock(return_value = None) + db.instance_get_floating_address = Mock(return_value = '1.1.1.200') + db.floating_ip_get_by_address = Mock(return_value = self.floating_ip1) + db.floating_ip_update = Mock(return_value = None) + db.instance_update = Mock(return_value = None) + + + # ---> test for nova.virt.libvirt_conn.nwfilter_for_instance_exists() + + def test01(self): + """01: libvirt.libvirtError occurs. """ + + self.manager._wrapped_conn = DummyLibvirtConn() + self.manager._test_connection = Mock(return_value=True) + self.manager._conn.nwfilterLookupByName = \ + Mock(side_effect=libvirt.libvirtError("ERR")) + ret = self.manager.nwfilter_for_instance_exists(self.instance1) + self.assertEqual(ret, False) + + def test02(self): + """02: libvirt.libvirtError not occurs. """ + + self.manager._wrapped_conn = DummyLibvirtConn() + self.manager._test_connection = Mock(return_value=True) + self.manager._conn.nwfilterLookupByName = \ + Mock(return_value=True) + ret = self.manager.nwfilter_for_instance_exists(self.instance1) + self.assertEqual(ret, True) + + # ---> test for nova.virt.libvirt_conn.live_migraiton() + + def test03(self): + """03: Unexpected exception occurs on finding volume on DB. """ + + utils.execute = Mock( side_effect=process.ProcessExecutionError('ERR') ) + + self.assertRaises(process.ProcessExecutionError, + self.manager.live_migration, + self.instance1, + 'host2') + + # ---> other case cannot be tested because live_migraiton + # is synchronized/asynchronized method are mixed together + + + # ---> test for nova.virt.libvirt_conn._post_live_migraiton + + def test04(self): + """04: instance_ref is not nova.db.sqlalchemy.models.Instances""" + + self.assertRaises(TypeError, + self.manager._post_live_migration, + "dummy string", + 'host2') + + def test05(self): + """05: db.instance_get_fixed_address return None""" + + db.instance_get_fixed_address = Mock( return_value=None ) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('fixed_ip is not found')) + self.assertEqual(c1 and c2, True) + + def test06(self): + """06: db.instance_get_fixed_address raises NotFound""" + + db.instance_get_fixed_address = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host2') + + def test07(self): + """07: db.instance_get_fixed_address raises Unknown exception""" + + db.instance_get_fixed_address = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test08(self): + """08: db.fixed_ip_update return NotFound. """ + + db.fixed_ip_update = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test09(self): + """09: db.fixed_ip_update return NotAuthorized. """ + db.fixed_ip_update = Mock( side_effect=exception.NotAuthorized('ERR') ) + self.assertRaises(exception.NotAuthorized, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test10(self): + """10: db.fixed_ip_update return Unknown exception. """ + db.fixed_ip_update = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test11(self): + """11: db.fixed_ip_get_network causes NotFound. """ + + db.fixed_ip_get_network = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host1') + + # not tested db.fixed_ip_get_network raises NotAuthorized + # because same test has been done at previous test. + + def test12(self): + """12: db.fixed_ip_get_network causes Unknown exception. """ + + db.fixed_ip_get_network = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test13(self): + """13: db.network_update raises Unknown exception. """ + db.network_update = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test14(self): + """14: db.instance_get_floating_address raises NotFound. """ + db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + + def test15(self): + """15: db.instance_get_floating_address returns None. """ + + db.instance_get_floating_address = Mock( return_value=None ) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('floating_ip is not found')) + self.assertEqual(c1 and c2, True) + + def test16(self): + """16: db.instance_get_floating_address raises NotFound. """ + + db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + def test17(self): + """17: db.instance_get_floating_address raises Unknown exception. """ + db.instance_get_floating_address = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + + def test18(self): + """18: db.floating_ip_get_by_address raises NotFound """ + + db.floating_ip_get_by_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + def test19(self): + """19: db.floating_ip_get_by_address raises Unknown exception. """ + db.floating_ip_get_by_address = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + + def test20(self): + """20: db.floating_ip_update raises Unknown exception. + """ + db.floating_ip_update = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + def test21(self): + """21: db.instance_update raises unknown exception. """ + + db.instance_update = Mock(side_effect=TypeError("ERR")) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def tearDown(self): + """common terminating method. """ + self.stderr.realFlush() + sys.stderr = self.stderrBak + #sys.stdout = self.stdoutBak + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.DEBUG) + #unittest.main() + + suite = unittest.TestLoader().loadTestsFromTestCase(LibvirtConnectionTestFunctions) + unittest.TextTestRunner(verbosity=2).run(suite) + + #suite = unittest.TestSuite() + #suite.addTest(LibvirtConnectionTestFunctions("test14")) + #suite.addTest(LibvirtConnectionTestFunctions("test16")) + #unittest.TextTestRunner(verbosity=2).run(suite) + + diff --git a/nova/livemigration_test/UT/nova-manage.test.py b/nova/livemigration_test/UT/nova-manage.test.py new file mode 100644 index 000000000..dabdba001 --- /dev/null +++ b/nova/livemigration_test/UT/nova-manage.test.py @@ -0,0 +1,318 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR='/opt/nova-2010.2' + +import sys +import unittest +import commands +import re + +from mock import Mock + +try : + print + print 'Testing %s/bin/nova-manage, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def flush(self): + self.buffer = '' + + +class NovaManageTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + + hostCmds = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + commands.getstatusoutput('cp -f %s/bin/nova-manage %s' % ( NOVA_DIR, self.getNovaManageCopyPath() )) + commands.getstatusoutput('touch %s' % self.getInitpyPath() ) + try : + import bin.novamanagetest + except: + print 'Fail to import nova-manage . check bin/nova-manage exists' + raise + + # replace stdout for checking nova-manage output + if self.stdout is None : + self.__class__.stdout = tmpStdout() + self.stdoutBak = sys.stdout + sys.stdout = self.stdout + + # prepare test data + self.setTestData() + + + def setTestData(self): + import bin.novamanagetest + + if self.hostCmds is None : + self.__class__.hostCmds = bin.novamanagetest.HostCommands() + self.instanceCmds = bin.novamanagetest.InstanceCommands() + + self.host1 = Host() + self.host1.__setitem__('name', 'host1') + + self.host2 = Host() + self.host2.__setitem__('name', 'host2') + + self.instance1 = Instance() + self.instance1.__setitem__('id', 1) + self.instance1.__setitem__('host', 'host1') + self.instance1.__setitem__('hostname', 'i-12345') + self.instance1.__setitem__('state', power_state.NOSTATE) + self.instance1.__setitem__('state_description', 'running') + + self.instance2 = Instance() + self.instance2.__setitem__('id', 2) + self.instance2.__setitem__('host', 'host1') + self.instance2.__setitem__('hostname', 'i-12345') + self.instance2.__setitem__('state', power_state.RUNNING) + self.instance2.__setitem__('state_description', 'pending') + + self.instance3 = Instance() + self.instance3.__setitem__('id', 3) + self.instance3.__setitem__('host', 'host1') + self.instance3.__setitem__('hostname', 'i-12345') + self.instance3.__setitem__('state', power_state.RUNNING) + self.instance3.__setitem__('state_description', 'running') + + db.host_get_all = Mock(return_value=[self.host1, self.host2]) + + def getInitpyPath(self): + return '%s/bin/__init__.py' % NOVA_DIR + + def getNovaManageCopyPath(self): + return '%s/bin/novamanagetest.py' % NOVA_DIR + + # -----> Test for nova-manage host list + + def test01(self): + """01: Got some host lists. """ + + self.hostCmds.list() + + c1 = (2 == self.stdout.buffer.count('\n')) + c2 = (0 <= self.stdout.buffer.find('host1')) + c3 = (0 <= self.stdout.buffer.find('host2')) + self.assertEqual(c1 and c2 and c3, True) + + def test02(self): + """02: Got empty lsit. """ + + db.host_get_all = Mock(return_value=[]) + self.hostCmds.list() + + # result should be empty + c = (0 == len(self.stdout.buffer) ) + self.assertEqual(c, True) + + def test03(self): + """03: Got notFound """ + + db.host_get_all = Mock(side_effect=exception.NotFound("ERR")) + self.assertRaises(exception.NotFound, self.hostCmds.list) + + # --------> Test For nova-manage host show + + def test04(self): + """04: args are not enough(nova-manage host show) """ + self.assertRaises(TypeError, self.hostCmds.show ) + + + def test05(self): + """05: nova-manage host show not-registered-host, and got an error""" + + rpc.call = Mock(return_value={'ret' : False, 'msg': 'ERR'} ) + self.hostCmds.show('host1') + self.assertEqual( self.stdout.buffer[:3]=='ERR', True ) + + + def test06(self): + """06: nova-manage host show registerd-host, and no project uses the host""" + + dic = {'ret': True, + 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, + 'usage': {}} + + rpc.call = Mock(return_value=dic ) + self.hostCmds.show('host1') + + # result should be : + # HOST PROJECT cpu mem(mb) disk(gb) + # host1 1 2 3 + line = self.stdout.buffer.split('\n')[1] + line = re.compile('\t+').sub(' ', line).strip() + c1 = ( 'host1 1 2 3' == line ) + c2 = ( self.stdout.buffer.count('\n') == 2 ) + + self.assertEqual( c1 and c2, True ) + + def test07(self): + """07: nova-manage host show registerd-host, + and some projects use the host + """ + dic = {'ret': True, + 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, + 'usage': {'p1': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, + 'p2': {'cpu':1, 'memory_mb':2, 'hdd_gb':3} }} + + rpc.call = Mock(return_value=dic ) + self.hostCmds.show('host1') + + # result should be : + # HOST PROJECT cpu mem(mb) disk(gb) + # host1 1 2 3 + # host1 p1 1 2 3 + # host1 p2 4 5 6 + line = self.stdout.buffer.split('\n')[1] + ret = re.compile('\t+').sub(' ', line).strip() + c1 = ( 'host1 1 2 3' == ret ) + + line = self.stdout.buffer.split('\n')[2] + line = re.compile('\t+').sub(' ', line).strip() + c2 = ( 'host1 p1 1 2 3' == line ) or ( 'host1 p2 1 2 3' == line ) + + line = self.stdout.buffer.split('\n')[3] + ret = re.compile('\t+').sub(' ', line).strip() + c3 = ( 'host1 p1 1 2 3' == ret ) or ( 'host1 p2 1 2 3' == ret ) + + self.assertEqual( c1 and c2 and c3, True ) + + def test08(self): + """08: nova-manage host show registerd-host, and rpc.call returns None + (unexpected error) + """ + rpc.call = Mock(return_value=None ) + self.hostCmds.show('host1') + c1 = ( 0 <= self.stdout.buffer.find('Unexpected error') ) + self.assertEqual( c1, True ) + + # ----------> Test for bin/nova-manage instance live_migration + + def test09(self): + """09: arguments are not enough(nova-manage instances live_migration) + """ + self.assertRaises(TypeError, self.instanceCmds.live_migration ) + + def test10(self): + """10: arguments are not enough(nova-manage instances live_migration ec2_id) + """ + self.assertRaises(TypeError, self.instanceCmds.live_migration, 'i-xxx' ) + + def test11(self): + """11: nova-manage instances live_migration ec2_id host, + where hostname is invalid + """ + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) + + def test12(self): + """12: nova-manage instances live_migration ec2_id(invalid id) host""" + + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( side_effect=exception.NotFound('ERR') ) + + self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) + + def test13(self): + """13: nova-manage instances live_migration ec2_id host, + but instance specifed by ec2 id is not running (state is not power_state.RUNNING) + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) + c1 = c2 = False + try : + self.instanceCmds.live_migration('i-12345', 'host1') + except SystemExit, e: + c1 = (1 == e.code) + c2 = (0 < self.stdout.buffer.find('is not running') ) + self.assertEqual( c1 and c2 , True ) + + + def test14(self): + """14: nova-manage instances live_migration ec2_id host, + but instance specifed by ec2 id is not running (state_description is not running) + """ + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) + c1 = c2 = False + try : + self.instanceCmds.live_migration('i-12345', 'host2') + except SystemExit, e: + c1 = (1 == e.code) + c2 = (0 < self.stdout.buffer.find('is not running') ) + self.assertEqual( c1 and c2 , True ) + + def test15(self): + """15: nova-manage instances live_migration ec2_id host, + but instance is running at the same host specifed above, so err should be occured. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + c1 = c2 = False + try : + self.instanceCmds.live_migration('i-12345', 'host1') + except SystemExit, e: + c1 = (2 == e.code) + c2 = (0 < self.stdout.buffer.find('is running now') ) + self.assertEqual( c1 and c2 , True ) + + def test16(self): + """16: nova-manage instances live_migration ec2_id host, + everything goes well, ang gets success messages. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + rpc.cast = Mock(return_value = None) + + self.instanceCmds.live_migration('i-12345', 'host2') + c1 = (0 <= self.stdout.buffer.find('Finished all procedure') ) + self.assertEqual( c1, True ) + + + def tearDown(self): + """common terminating method. """ + commands.getstatusoutput('rm -rf %s' % self.getInitpyPath() ) + commands.getstatusoutput('rm -rf %s' % self.getNovaManageCopyPath() ) + sys.stdout.flush() + sys.stdout = self.stdoutBak + +if __name__ == '__main__': + #unittest.main() + suite = unittest.TestLoader().loadTestsFromTestCase(NovaManageTestFunctions) + unittest.TextTestRunner(verbosity=3).run(suite) + + diff --git a/nova/livemigration_test/UT/schedulerManager.test.py b/nova/livemigration_test/UT/schedulerManager.test.py new file mode 100644 index 000000000..2fe4d0994 --- /dev/null +++ b/nova/livemigration_test/UT/schedulerManager.test.py @@ -0,0 +1,360 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR='/opt/nova-2010.2' + +import sys +import unittest +import commands +import re + +from mock import Mock + +try : + print + print 'Checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.scheduler.manager import SchedulerManager + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + +except: + print 'set PYTHONPATH to nova-install-dir' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def flush(self): + self.buffer = '' + + +class SchedulerTestFunctions(unittest.TestCase): + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + self.host = 'openstack2-api' + self.manager = SchedulerManager(host=self.host) + + self.setTestData() + + def setTestData(self): + + self.host1 = Host() + self.host1.__setitem__('name', 'host1') + self.host1.__setitem__('cpu', 5) + self.host1.__setitem__('memory_mb', 20480) + self.host1.__setitem__('hdd_gb', 876) + + self.host2 = Host() + self.host2.__setitem__('name', 'host2') + self.host2.__setitem__('cpu', 5) + self.host2.__setitem__('memory_mb', 20480) + self.host2.__setitem__('hdd_gb', 876) + + self.instance1 = Instance() + for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance1.__setitem__(key, val) + + + self.instance2 = Instance() + for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance2.__setitem__(key, val) + + + self.instance3 = Instance() + for key, val in [ ('id', 3), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance3.__setitem__(key, val) + + self.instance4 = Instance() + for key, val in [ ('id', 4), ('host', 'host2'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance4.__setitem__(key, val) + + self.instance5 = Instance() + for key, val in [ ('id', 5), ('host', 'host2'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance5.__setitem__(key, val) + + self.instance6 = Instance() + for key, val in [ ('id', 6), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance6.__setitem__(key, val) + + self.instance7 = Instance() + for key, val in [ ('id', 7), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 18432), ('local_gb', 5) ]: + self.instance7.__setitem__(key, val) + + self.instance8 = Instance() + for key, val in [ ('id', 8), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 866) ]: + self.instance8.__setitem__(key, val) + + + + def check_format(self, val): + """check result format of show_host_resource """ + + if dict != type(val) : + sys.stderr.write('return value is not dict') + return False + + if not val.has_key('ret'): + sys.stderr.write('invalid format(missing "ret"). ') + return False + + if not val['ret'] : + if not val.has_key('msg') : + sys.stderr.write( 'invalid format(missing "msg").' ) + return False + + else : + if not val.has_key('phy_resource') : + sys.stderr.write('invalid format(missing "phy_resource"). ') + return False + + if not val.has_key('usage'): + sys.stderr.write('invalid format(missing "usage"). ') + return False + + if not self._check_format(val['phy_resource']): + return False + + for key, dic in val['usage'].items() : + if not self._check_format(dic): + return False + return True + + def _check_format(self, val): + if dict != type(val) : + sys.stderr.write('return value is not dict') + return False + + for key in ['cpu', 'memory_mb', 'hdd_gb']: + if not val.has_key(key) : + sys.stderr.write('invalid format(missing "%s"). ' % key ) + return False + + return True + + # ---> test for nova.scheduler.manager.show_host_resource() + + def test01(self): + """01: get NotFound exception when dest host not found on DB """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + result = self.manager.show_host_resource(ctxt, 'not-registered-host') + c1 = ( not result['ret'] ) + c2 = ( 0 == result['msg'].find('No such') ) + self.assertEqual(c1 and c2, True) + + def test02(self): + """02: get other exception if unexpected err. """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, self.manager.show_host_resource, ctxt, 'host1' ) + + def test03(self): + """03: no instance found on dest host. """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( return_value = self.host1 ) + db.instance_get_all_by_host = Mock( return_value=[]) + ret= self.manager.show_host_resource(ctxt, 'host1') + + c1 = self.check_format(ret) + v = ret['phy_resource'] + c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) + c3 = ( 0 == len(ret['usage']) ) + + self.assertEqual(c1 and c2 and c3, True) + + def test04(self): + """04: some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.host_get_by_name = Mock( return_value = self.host1 ) + db.instance_get_all_by_host = Mock( return_value=[ self.instance1, + self.instance2, + self.instance3] ) + + db.instance_get_vcpu_sum_by_host_and_project = Mock(return_value=3) + db.instance_get_memory_sum_by_host_and_project = Mock(return_value=1024) + db.instance_get_disk_sum_by_host_and_project = Mock(return_value=5) + + ret= self.manager.show_host_resource(ctxt, 'host1') + + c1 = self.check_format(ret) + v = ret['phy_resource'] + c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) + c3 = ( 2 == len(ret['usage']) ) + c4 = ( self.instance1['project_id'] in ret['usage'].keys()) + c5 = ( self.instance3['project_id'] in ret['usage'].keys()) + + self.assertEqual(c1 and c2 and c3 and c4 and c5, True) + + + # ---> test for nova.scheduler.manager.has_enough_resource() + def test05(self): + """05: when cpu is exccded some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance6) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + def test06(self): + """06: when memory is exccded some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance7) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + def test07(self): + """07: when hdd is exccded some instance found on dest host. """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance8) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + def test08(self): + """08: everything goes well. (instance_get_all_by_host returns list)""" + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, True) + + + def test09(self): + """09: everything goes well(instance_get_all_by_host returns[]). """ + + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [] ) + + ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, True) + + + # ---> test for nova.scheduler.manager.live_migration() + + + def test10(self): + """10: instance_get_by_internal_id issue NotFound. """ + # Mocks for has_enough_resource() + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance8) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + # Mocks for live_migration()db.instance_get_by_internal_id + # (any Mock is ok here. important mock is all above) + db.instance_get_by_internal_id = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.live_migration, + ctxt, + 'i-12345', + 'host1') + + + def test11(self): + """11: return False if host doesnt have enough resource. """ + + # Mocks for has_enough_resource() + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance8) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + # Mocks for live_migration()db.instance_get_by_internal_id + # (any Mock is ok here. important mock is all above) + db.instance_get_by_internal_id = Mock(return_value = self.instance8) + db.instance_set_state = Mock(return_value = True) + rpc_cast = Mock(return_value = True) + + ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, False) + + + + def test12(self): + """12: everything goes well. """ + + # Mocks for has_enough_resource() + ctxt = context.get_admin_context() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + # Mocks for live_migration()db.instance_get_by_internal_id + # (any Mock is ok here. important mock is all above) + db.instance_get_by_internal_id = Mock(return_value = self.instance8) + db.instance_set_state = Mock(return_value = True) + rpc.cast = Mock(return_value = True) + + ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') + self.assertEqual(ret, True) + + + def tearDown(self): + """common terminating method. """ + #sys.stdout = self.stdoutBak + pass + +if __name__ == '__main__': + #unittest.main() + suite = unittest.TestLoader().loadTestsFromTestCase(SchedulerTestFunctions) + unittest.TextTestRunner(verbosity=3).run(suite) + + diff --git a/nova/livemigration_test/UT/testCase_UT.xls b/nova/livemigration_test/UT/testCase_UT.xls new file mode 100644 index 000000000..0524526b6 Binary files /dev/null and b/nova/livemigration_test/UT/testCase_UT.xls differ -- cgit From 4ff2da231d485598232d9aacc41538950005ac34 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Wed, 22 Dec 2010 16:43:47 -0800 Subject: Basic Easy API functionality --- nova/api/easy.py | 163 ++++++++++++++++++++++++++++++++++++++++++++ nova/tests/easy_unittest.py | 85 +++++++++++++++++++++++ nova/wsgi.py | 31 +++++++-- 3 files changed, 273 insertions(+), 6 deletions(-) create mode 100644 nova/api/easy.py create mode 100644 nova/tests/easy_unittest.py (limited to 'nova') diff --git a/nova/api/easy.py b/nova/api/easy.py new file mode 100644 index 000000000..a284e9685 --- /dev/null +++ b/nova/api/easy.py @@ -0,0 +1,163 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""Public HTTP interface that allows services to self-register. + +The general flow of a request is: + - Request is parsed into WSGI bits. + - Some middleware checks authentication. + - Routing takes place based on the URL to find a controller. + (/controller/method) + - Parameters are parsed from the request and passed to a method on the + controller as keyword arguments. + - Optionally json_body is decoded to provide all the parameters. + - Actual work is done and a result is returned. + - That result is turned into json and returned. + +""" + +import json +import urllib + +import routes +import webob + +from nova import context +from nova import flags +from nova import wsgi + +# prxy compute_api in amazon tests + + +EASY_ROUTES = {} + + +def register_service(path, handle): + EASY_ROUTES[path] = handle + + +class DelegatedAuthMiddleware(wsgi.Middleware): + def process_request(self, request): + os_user = request.headers['X-OpenStack-User'] + os_project = request.headers['X-OpenStack-Project'] + context_ref = context.RequestContext(user=os_user, project=os_project) + request.environ['openstack.context'] = context_ref + + +class JsonParamsMiddleware(wsgi.Middleware): + def process_request(self, request): + if 'json' not in request.params: + return + + params_json = request.params['json'] + params_parsed = json.loads(params_json) + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ['openstack.params'] = params + + +class ReqParamsMiddleware(wsgi.Middleware): + def process_request(self, request): + params_parsed = request.params + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ['openstack.params'] = params + + +class SundayMorning(wsgi.Router): + def __init__(self, mapper=None): + if mapper is None: + mapper = routes.Mapper() + + self._load_registered_routes(mapper) + super(SundayMorning, self).__init__(mapper=mapper) + + def _load_registered_routes(self, mapper): + for route in EASY_ROUTES: + mapper.connect('/%s/{action}' % route, + controller=ServiceWrapper(EASY_ROUTES[route])) + + +class ServiceWrapper(wsgi.Controller): + def __init__(self, service_handle): + self.service_handle = service_handle + + @webob.dec.wsgify + def __call__(self, req): + arg_dict = req.environ['wsgiorg.routing_args'][1] + action = arg_dict['action'] + del arg_dict['action'] + + context = req.environ['openstack.context'] + # allow middleware up the stack to override the params + params = {} + if 'openstack.params' in req.environ: + params = req.environ['openstack.params'] + + # TODO(termie): do some basic normalization on methods + method = getattr(self.service_handle, action) + + result = method(context, **params) + if type(result) is dict: + return self._serialize(result, req) + else: + return result + + +class Proxy(object): + """Pretend an Easy API endpoint is an object.""" + def __init__(self, app, prefix=None): + self.app = app + self.prefix = prefix + + def __do_request(self, path, context, **kwargs): + req = webob.Request.blank(path) + req.method = 'POST' + req.body = urllib.urlencode({'json': json.dumps(kwargs)}) + req.environ['openstack.context'] = context + resp = req.get_response(self.app) + try: + return json.loads(resp.body) + except Exception: + return resp.body + + def __getattr__(self, key): + if self.prefix is None: + return self.__class__(self.app, key) + + def _wrapper(context, **kwargs): + return self.__do_request('/%s/%s' % (self.prefix, key), + context, + **kwargs) + _wrapper.func_name = key + return _wrapper + + + diff --git a/nova/tests/easy_unittest.py b/nova/tests/easy_unittest.py new file mode 100644 index 000000000..ed223831f --- /dev/null +++ b/nova/tests/easy_unittest.py @@ -0,0 +1,85 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""Tests for Easy API.""" + +import json +import logging + +import webob + +from nova import context +from nova import exception +from nova import test +from nova import utils +from nova.api import easy + + +class FakeService(object): + def echo(self, context, data): + return {'data': data} + + def context(self, context): + return {'user': context.user_id, + 'project': context.project_id} + + +class EasyTestCase(test.TestCase): + def setUp(self): + super(EasyTestCase, self).setUp() + easy.register_service('fake', FakeService()) + self.router = easy.ReqParamsMiddleware( + easy.JsonParamsMiddleware( + easy.SundayMorning())) + self.auth_router = easy.DelegatedAuthMiddleware(self.router) + self.context = context.RequestContext('user1', 'proj1') + + def tearDown(self): + easy.EASY_ROUTES = {} + + def test_delegated_auth(self): + req = webob.Request.blank('/fake/context') + req.headers['X-OpenStack-User'] = 'user1' + req.headers['X-OpenStack-Project'] = 'proj1' + resp = req.get_response(self.auth_router) + data = json.loads(resp.body) + self.assertEqual(data['user'], 'user1') + self.assertEqual(data['project'], 'proj1') + + def test_json_params(self): + req = webob.Request.blank('/fake/echo') + req.environ['openstack.context'] = self.context + req.method = 'POST' + req.body = 'json=%s' % json.dumps({'data': 'foo'}) + resp = req.get_response(self.router) + resp_parsed = json.loads(resp.body) + self.assertEqual(resp_parsed['data'], 'foo') + + def test_req_params(self): + req = webob.Request.blank('/fake/echo') + req.environ['openstack.context'] = self.context + req.method = 'POST' + req.body = 'data=foo' + resp = req.get_response(self.router) + resp_parsed = json.loads(resp.body) + self.assertEqual(resp_parsed['data'], 'foo') + + def test_proxy(self): + proxy = easy.Proxy(self.router) + rv = proxy.fake.echo(self.context, data='baz') + self.assertEqual(rv['data'], 'baz') diff --git a/nova/wsgi.py b/nova/wsgi.py index c7ee9ed14..2ad27dd7e 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -104,20 +104,39 @@ class Application(object): class Middleware(Application): - """ - Base WSGI middleware wrapper. These classes require an application to be + """Base WSGI middleware. + + Modelled after Django's middleware this class allows you to + These classes require an application to be initialized that will be called next. By default the middleware will simply call its wrapped app, or you can override __call__ to customize its behavior. """ - def __init__(self, application): # pylint: disable-msg=W0231 + def __init__(self, application): self.application = application + def process_request(self, req): + """Called on each request. + + If this returns None, the next application down the stack will be + executed. If it returns a response then that response will be returned + and execution will stop here. + + """ + return None + + def process_response(self, response): + """Do whatever you'd like to the response.""" + return response + @webob.dec.wsgify - def __call__(self, req): # pylint: disable-msg=W0221 - """Override to implement middleware behavior.""" - return self.application + def __call__(self, req): + response = self.process_request(req) + if response: + return response + response = req.get_response(self.application) + return self.process_response(response) class Debug(Middleware): -- cgit From 43e9f8727af618fc3e50308cba95b27c67ee83c5 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Wed, 22 Dec 2010 16:52:16 -0800 Subject: remove some yields that snuck in --- nova/tests/cloud_unittest.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'nova') diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 70d2c44da..1398a9862 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -140,15 +140,15 @@ class CloudTestCase(test.TestCase): kwargs = {'image_id': image_id, 'instance_type': instance_type, 'max_count': max_count} - rv = yield self.cloud.run_instances(self.context, **kwargs) + rv = self.cloud.run_instances(self.context, **kwargs) instance_id = rv['instancesSet'][0]['instanceId'] - output = yield self.cloud.get_console_output(context=self.context, + output = self.cloud.get_console_output(context=self.context, instance_id=[instance_id]) self.assertEquals(b64decode(output['output']), 'FAKE CONSOLE OUTPUT') # TODO(soren): We need this until we can stop polling in the rpc code # for unit tests. greenthread.sleep(0.3) - rv = yield self.cloud.terminate_instances(self.context, [instance_id]) + rv = self.cloud.terminate_instances(self.context, [instance_id]) def test_key_generation(self): result = self._create_key('test') @@ -186,7 +186,7 @@ class CloudTestCase(test.TestCase): kwargs = {'image_id': image_id, 'instance_type': instance_type, 'max_count': max_count} - rv = yield self.cloud.run_instances(self.context, **kwargs) + rv = self.cloud.run_instances(self.context, **kwargs) # TODO: check for proper response instance_id = rv['reservationSet'][0].keys()[0] instance = rv['reservationSet'][0][instance_id][0] @@ -209,7 +209,7 @@ class CloudTestCase(test.TestCase): for instance in reservations[reservations.keys()[0]]: instance_id = instance['instance_id'] logging.debug("Terminating instance %s" % instance_id) - rv = yield self.compute.terminate_instance(instance_id) + rv = self.compute.terminate_instance(instance_id) def test_instance_update_state(self): def instance(num): -- cgit From 70d254c626e925f6de8408f0ca70f3de28a7307a Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Wed, 22 Dec 2010 17:53:42 -0800 Subject: added tests to ensure the easy api works as a backend for Compute API --- nova/api/easy.py | 10 +++++----- nova/api/ec2/cloud.py | 34 ++++++++++++++++++++-------------- nova/compute/api.py | 19 +++++++++++++++---- nova/tests/cloud_unittest.py | 2 ++ nova/tests/compute_unittest.py | 9 ++++++--- nova/tests/easy_unittest.py | 19 ++++++++++++++++++- nova/utils.py | 31 +++++++++++++++++++++++++++++++ nova/wsgi.py | 6 +++--- 8 files changed, 100 insertions(+), 30 deletions(-) (limited to 'nova') diff --git a/nova/api/easy.py b/nova/api/easy.py index a284e9685..9ef487739 100644 --- a/nova/api/easy.py +++ b/nova/api/easy.py @@ -31,7 +31,6 @@ The general flow of a request is: """ -import json import urllib import routes @@ -39,6 +38,7 @@ import webob from nova import context from nova import flags +from nova import utils from nova import wsgi # prxy compute_api in amazon tests @@ -65,7 +65,7 @@ class JsonParamsMiddleware(wsgi.Middleware): return params_json = request.params['json'] - params_parsed = json.loads(params_json) + params_parsed = utils.loads(params_json) params = {} for k, v in params_parsed.iteritems(): if k in ('self', 'context'): @@ -125,7 +125,7 @@ class ServiceWrapper(wsgi.Controller): method = getattr(self.service_handle, action) result = method(context, **params) - if type(result) is dict: + if type(result) is dict or type(result) is list: return self._serialize(result, req) else: return result @@ -140,11 +140,11 @@ class Proxy(object): def __do_request(self, path, context, **kwargs): req = webob.Request.blank(path) req.method = 'POST' - req.body = urllib.urlencode({'json': json.dumps(kwargs)}) + req.body = urllib.urlencode({'json': utils.dumps(kwargs)}) req.environ['openstack.context'] = context resp = req.get_response(self.app) try: - return json.loads(resp.body) + return utils.loads(resp.body) except Exception: return resp.body diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index e09261f00..61c6c0da1 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -118,7 +118,8 @@ class CloudController(object): def _get_mpi_data(self, context, project_id): result = {} - for instance in self.compute_api.get_instances(context, project_id): + for instance in self.compute_api.get_instances(context, + project_id=project_id): if instance['fixed_ip']: line = '%s slots=%d' % (instance['fixed_ip']['address'], instance['vcpus']) @@ -442,7 +443,8 @@ class CloudController(object): # instance_id is passed in as a list of instances ec2_id = instance_id[0] internal_id = ec2_id_to_internal_id(ec2_id) - instance_ref = self.compute_api.get_instance(context, internal_id) + instance_ref = self.compute_api.get_instance(context, + instance_id=internal_id) output = rpc.call(context, '%s.%s' % (FLAGS.compute_topic, instance_ref['host']), @@ -541,7 +543,8 @@ class CloudController(object): if volume_ref['attach_status'] == "attached": raise exception.ApiError(_("Volume is already attached")) internal_id = ec2_id_to_internal_id(instance_id) - instance_ref = self.compute_api.get_instance(context, internal_id) + instance_ref = self.compute_api.get_instance(context, + instance_id=internal_id) host = instance_ref['host'] rpc.cast(context, db.queue_get_for(context, FLAGS.compute_topic, host), @@ -722,14 +725,15 @@ class CloudController(object): def associate_address(self, context, instance_id, public_ip, **kwargs): internal_id = ec2_id_to_internal_id(instance_id) - instance_ref = self.compute_api.get_instance(context, internal_id) + instance_ref = self.compute_api.get_instance(context, + instance_id=internal_id) fixed_address = db.instance_get_fixed_address(context, instance_ref['id']) floating_ip_ref = db.floating_ip_get_by_address(context, public_ip) # NOTE(vish): Perhaps we should just pass this on to compute and # let compute communicate with network. - network_topic = self.compute_api.get_network_topic(context, - internal_id) + network_topic = self.compute_api.get_network_topic( + context, instance_id=internal_id) rpc.cast(context, network_topic, {"method": "associate_floating_ip", @@ -754,8 +758,9 @@ class CloudController(object): def run_instances(self, context, **kwargs): max_count = int(kwargs.get('max_count', 1)) instances = self.compute_api.create_instances(context, - instance_types.get_by_type(kwargs.get('instance_type', None)), - kwargs['image_id'], + instance_type=instance_types.get_by_type( + kwargs.get('instance_type', None)), + image_id=kwargs['image_id'], min_count=int(kwargs.get('min_count', max_count)), max_count=max_count, kernel_id=kwargs.get('kernel_id', None), @@ -765,7 +770,7 @@ class CloudController(object): key_name=kwargs.get('key_name'), user_data=kwargs.get('user_data'), security_group=kwargs.get('security_group'), - generate_hostname=internal_id_to_ec2_id) + hostname_format='ec2') return self._format_run_instances(context, instances[0]['reservation_id']) @@ -775,26 +780,26 @@ class CloudController(object): logging.debug("Going to start terminating instances") for ec2_id in instance_id: internal_id = ec2_id_to_internal_id(ec2_id) - self.compute_api.delete_instance(context, internal_id) + self.compute_api.delete_instance(context, instance_id=internal_id) return True def reboot_instances(self, context, instance_id, **kwargs): """instance_id is a list of instance ids""" for ec2_id in instance_id: internal_id = ec2_id_to_internal_id(ec2_id) - self.compute_api.reboot(context, internal_id) + self.compute_api.reboot(context, instance_id=internal_id) return True def rescue_instance(self, context, instance_id, **kwargs): """This is an extension to the normal ec2_api""" internal_id = ec2_id_to_internal_id(instance_id) - self.compute_api.rescue(context, internal_id) + self.compute_api.rescue(context, instance_id=internal_id) return True def unrescue_instance(self, context, instance_id, **kwargs): """This is an extension to the normal ec2_api""" internal_id = ec2_id_to_internal_id(instance_id) - self.compute_api.unrescue(context, internal_id) + self.compute_api.unrescue(context, instance_id=internal_id) return True def update_instance(self, context, ec2_id, **kwargs): @@ -805,7 +810,8 @@ class CloudController(object): changes[field] = kwargs[field] if changes: internal_id = ec2_id_to_internal_id(ec2_id) - inst = self.compute_api.get_instance(context, internal_id) + inst = self.compute_api.get_instance(context, + instance_id=internal_id) db.instance_update(context, inst['id'], kwargs) return True diff --git a/nova/compute/api.py b/nova/compute/api.py index 4953fe559..5f18539a3 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -36,10 +36,19 @@ from nova.db import base FLAGS = flags.FLAGS -def generate_default_hostname(internal_id): +def id_to_default_hostname(internal_id): """Default function to generate a hostname given an instance reference.""" return str(internal_id) +def id_to_ec2_hostname(internal_id): + digits = [] + while internal_id != 0: + internal_id, remainder = divmod(internal_id, 36) + digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) + return "i-%s" % ''.join(reversed(digits)) + +HOSTNAME_FORMATTERS = {'default': id_to_default_hostname, + 'ec2': id_to_ec2_hostname} class ComputeAPI(base.Base): """API for interacting with the compute manager.""" @@ -75,7 +84,7 @@ class ComputeAPI(base.Base): display_name='', description='', key_name=None, key_data=None, security_group='default', user_data=None, - generate_hostname=generate_default_hostname): + hostname_format='default'): """Create the number of instances requested if quote and other arguments check out ok.""" @@ -144,6 +153,7 @@ class ComputeAPI(base.Base): elevated = context.elevated() instances = [] + generate_hostname = HOSTNAME_FORMATTERS[hostname_format] logging.debug(_("Going to run %s instances..."), num_instances) for num in range(num_instances): instance = dict(mac_address=utils.generate_mac(), @@ -177,7 +187,7 @@ class ComputeAPI(base.Base): "args": {"topic": FLAGS.compute_topic, "instance_id": instance_id}}) - return instances + return [dict(x.iteritems()) for x in instances] def ensure_default_security_group(self, context): """ Create security group for the security context if it @@ -254,7 +264,8 @@ class ComputeAPI(base.Base): return self.db.instance_get_all(context) def get_instance(self, context, instance_id): - return self.db.instance_get_by_internal_id(context, instance_id) + rv = self.db.instance_get_by_internal_id(context, instance_id) + return dict(rv.iteritems()) def reboot(self, context, instance_id): """Reboot the given instance.""" diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 1398a9862..4439c131c 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -22,6 +22,7 @@ import logging from M2Crypto import BIO from M2Crypto import RSA import os +import shutil import tempfile import time @@ -293,6 +294,7 @@ class CloudTestCase(test.TestCase): self.assertEqual('Foo Img', img.metadata['description']) self._fake_set_image_description(self.context, 'ami-testing', '') self.assertEqual('', img.metadata['description']) + shutil.rmtree(pathdir) def test_update_of_instance_display_fields(self): inst = db.instance_create(self.context, {}) diff --git a/nova/tests/compute_unittest.py b/nova/tests/compute_unittest.py index 348bb3351..0bebd1c5e 100644 --- a/nova/tests/compute_unittest.py +++ b/nova/tests/compute_unittest.py @@ -75,7 +75,7 @@ class ComputeTestCase(test.TestCase): ref = self.compute_api.create_instances(self.context, FLAGS.default_instance_type, None, **instance) try: - self.assertNotEqual(ref[0].display_name, None) + self.assertNotEqual(ref[0]['display_name'], None) finally: db.instance_destroy(self.context, ref[0]['id']) @@ -87,9 +87,12 @@ class ComputeTestCase(test.TestCase): 'project_id': self.project.id} group = db.security_group_create(self.context, values) ref = self.compute_api.create_instances(self.context, - FLAGS.default_instance_type, None, security_group=['default']) + instance_type=FLAGS.default_instance_type, + image_id=None, + security_group=['default']) try: - self.assertEqual(len(ref[0]['security_groups']), 1) + self.assertEqual(len(db.security_group_get_by_instance( + self.context, ref[0]['id'])), 1) finally: db.security_group_destroy(self.context, group['id']) db.instance_destroy(self.context, ref[0]['id']) diff --git a/nova/tests/easy_unittest.py b/nova/tests/easy_unittest.py index ed223831f..81990d842 100644 --- a/nova/tests/easy_unittest.py +++ b/nova/tests/easy_unittest.py @@ -28,7 +28,8 @@ from nova import exception from nova import test from nova import utils from nova.api import easy - +from nova.compute import api as compute_api +from nova.tests import cloud_unittest class FakeService(object): def echo(self, context, data): @@ -83,3 +84,19 @@ class EasyTestCase(test.TestCase): proxy = easy.Proxy(self.router) rv = proxy.fake.echo(self.context, data='baz') self.assertEqual(rv['data'], 'baz') + + +class EasyCloudTestCase(cloud_unittest.CloudTestCase): + def setUp(self): + super(EasyCloudTestCase, self).setUp() + compute_handle = compute_api.ComputeAPI(self.cloud.network_manager, + self.cloud.image_service) + easy.register_service('compute', compute_handle) + self.router = easy.JsonParamsMiddleware(easy.SundayMorning()) + proxy = easy.Proxy(self.router) + self.cloud.compute_api = proxy.compute + + def tearDown(self): + super(EasyCloudTestCase, self).tearDown() + easy.EASY_ROUTES = {} + diff --git a/nova/utils.py b/nova/utils.py index b9045a50c..7a98ffa5a 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -22,6 +22,7 @@ System-level utilities and helper functions. import datetime import inspect +import json import logging import os import random @@ -361,3 +362,33 @@ def utf8(value): return value.encode("utf-8") assert isinstance(value, str) return value + + +def to_primitive(value): + if type(value) is type([]) or type(value) is type((None,)): + o = [] + for v in value: + o.append(to_primitive(v)) + return o + elif type(value) is type({}): + o = {} + for k, v in value.iteritems(): + o[k] = to_primitive(v) + return o + elif isinstance(value, datetime.datetime): + return str(value) + else: + return value + + +def dumps(value): + try: + return json.dumps(value) + except TypeError: + pass + + return json.dumps(to_primitive(value)) + + +def loads(s): + return json.loads(s) diff --git a/nova/wsgi.py b/nova/wsgi.py index 2ad27dd7e..c40f043f9 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -21,7 +21,6 @@ Utility methods for working with WSGI servers """ -import json import logging import sys from xml.dom import minidom @@ -35,6 +34,7 @@ import webob import webob.dec import webob.exc +from nova import utils logging.getLogger("routes.middleware").addHandler(logging.StreamHandler()) @@ -322,7 +322,7 @@ class Serializer(object): try: is_xml = (datastring[0] == '<') if not is_xml: - return json.loads(datastring) + return utils.loads(datastring) return self._from_xml(datastring) except: return None @@ -355,7 +355,7 @@ class Serializer(object): return result def _to_json(self, data): - return json.dumps(data) + return utils.dumps(data) def _to_xml(self, data): metadata = self.metadata.get('application/xml', {}) -- cgit From 7c1b3ef521c652ce375390a1ecb04a60d1f100f0 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Wed, 22 Dec 2010 18:04:35 -0800 Subject: remove some notes --- nova/api/easy.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova') diff --git a/nova/api/easy.py b/nova/api/easy.py index 9ef487739..1be52069f 100644 --- a/nova/api/easy.py +++ b/nova/api/easy.py @@ -41,8 +41,6 @@ from nova import flags from nova import utils from nova import wsgi -# prxy compute_api in amazon tests - EASY_ROUTES = {} -- cgit From a1b5220879632d093f450413f96668a8f77c0613 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Thu, 23 Dec 2010 12:04:19 -0800 Subject: adds a reflection api --- nova/api/easy.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/easy.py b/nova/api/easy.py index 1be52069f..0e4f8a892 100644 --- a/nova/api/easy.py +++ b/nova/api/easy.py @@ -31,6 +31,7 @@ The general flow of a request is: """ +import inspect import urllib import routes @@ -102,6 +103,52 @@ class SundayMorning(wsgi.Router): mapper.connect('/%s/{action}' % route, controller=ServiceWrapper(EASY_ROUTES[route])) + +class Reflection(object): + def __init__(self): + self._methods = {} + + def _gather_methods(self): + methods = {} + for route, handler in EASY_ROUTES.iteritems(): + for k in dir(handler): + if k.startswith('_'): + continue + f = getattr(handler, k) + if not callable(f): + continue + + # bunch of ugly formatting stuff + argspec = inspect.getargspec(f) + args = [x for x in argspec[0] if x != 'self' and x != 'context'] + defaults = argspec[3] and argspec[3] or [] + args_r = list(reversed(args)) + defaults_r = list(reversed(defaults)) + args_out = [] + while args_r: + if defaults_r: + args_out.append((args_r.pop(0), defaults_r.pop(0))) + else: + args_out.append(str(args_r.pop(0))) + + methods['/%s/%s' % (route, k)] = { + 'name': k, + 'args': list(reversed(args_out))} + return methods + + def get_methods(self, context): + if not self._methods: + self._methods = self._gather_methods() + + method_list = self._methods.keys() + method_list.sort() + return {'methods': method_list} + + def get_method_info(self, context, method): + if not self._methods: + self._methods = self._gather_methods() + return self._methods[method] + class ServiceWrapper(wsgi.Controller): def __init__(self, service_handle): @@ -148,7 +195,7 @@ class Proxy(object): def __getattr__(self, key): if self.prefix is None: - return self.__class__(self.app, key) + return self.__class__(self.app, prefix=key) def _wrapper(context, **kwargs): return self.__do_request('/%s/%s' % (self.prefix, key), @@ -156,6 +203,3 @@ class Proxy(object): **kwargs) _wrapper.func_name = key return _wrapper - - - -- cgit From f983884dd262f46907f80a04121d957347881240 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 24 Dec 2010 15:09:05 +0900 Subject: nova.compute.managerがこれまでの修正でデグレしていたので修正 CPUID, その他のチェックルーチンをnova.scheduler.manager.live_migrationに追加 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/compute/manager.py | 15 +- nova/db/sqlalchemy/models.py | 8 +- nova/livemigration_test/SI/testCase_SI.xls | Bin 35840 -> 43520 bytes .../SI/testParameterSheet_SI.xls | Bin 464384 -> 464384 bytes nova/livemigration_test/UT/computeManager.test.py | 10 +- .../UT/libvirtConnection.test.py | 10 +- nova/livemigration_test/UT/nova-manage.test.py | 77 ++++++--- .../livemigration_test/UT/schedulerManager.test.py | 192 +++++++++++++-------- nova/livemigration_test/UT/testCase_UT.xls | Bin 195072 -> 202752 bytes nova/scheduler/manager.py | 70 ++++++-- nova/service.py | 17 +- nova/virt/libvirt_conn.py | 40 ++++- 12 files changed, 315 insertions(+), 124 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index bad525115..c2c532c6d 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -105,7 +105,7 @@ class ComputeManager(manager.Manager): self.network_manager.setup_compute_network(context, instance_id) self.db.instance_update(context, instance_id, - {'host': self.host}) + {'host': self.host, 'launch_at':self.host}) # TODO(vish) check to make sure the availability zone matches self.db.instance_set_state(context, @@ -261,18 +261,19 @@ class ComputeManager(manager.Manager): self.db.volume_detached(context, volume_id) defer.returnValue(True) - def get_vcpu_number(self): - """Get the number of vcpu on physical computer.""" - return self.driver.get_vcpu_number() + def compareCPU(self, context, xml): + """ Check the host cpu is compatible to a cpu given by xml.""" + logging.warn('good!') + return self.driver.compareCPU(xml) - def get_mem_size(self): + def get_memory_mb(self): """Get the memory size of physical computer .""" meminfo = open('/proc/meminfo').read().split() idx = meminfo.index('MemTotal:') # transforming kb to mb. return int(meminfo[idx + 1]) / 1024 - def get_hdd_size(self): + def get_local_gb(self): """Get the hdd size of physical computer .""" hddinfo = os.statvfs(FLAGS.instances_path) return hddinfo.f_bsize * hddinfo.f_blocks / 1024 / 1024 / 1024 @@ -315,7 +316,7 @@ class ComputeManager(manager.Manager): self.driver.setup_nwfilters_for_instance(instance_ref) # 5. bridge settings - self.network_manager.setup_compute_network(instance_id) + self.network_manager.setup_compute_network(context, instance_id) return True def nwfilter_for_instance_exists(self, context, instance_id): diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index db6f51948..7f3a58bcb 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -147,7 +147,9 @@ class Host(BASE, NovaBase): vcpus = Column(Integer, nullable=False, default=-1) memory_mb = Column(Integer, nullable=False, default=-1) local_gb = Column(Integer, nullable=False, default=-1) - #cpuid = Column(Integer, nullable=False) + hypervisor_type = Column(String(128)) + hypervisor_version = Column(Integer, nullable=False, default=-1) + cpu_info = Column(String(1024)) deleted = Column(Boolean, default=False) # C: when calling service_create() # D: never deleted. instead of deleting cloumn "deleted" is true @@ -232,6 +234,10 @@ class Instance(BASE, NovaBase): display_name = Column(String(255)) display_description = Column(String(255)) + # To remember at which host a instance booted. + # An instance may moved to other host by live migraiton. + launch_at = Column(String(255)) + # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such # vmstate_state = running, halted, suspended, paused diff --git a/nova/livemigration_test/SI/testCase_SI.xls b/nova/livemigration_test/SI/testCase_SI.xls index 723363c1e..65cf96fd7 100644 Binary files a/nova/livemigration_test/SI/testCase_SI.xls and b/nova/livemigration_test/SI/testCase_SI.xls differ diff --git a/nova/livemigration_test/SI/testParameterSheet_SI.xls b/nova/livemigration_test/SI/testParameterSheet_SI.xls index 192d9705b..400b43b43 100644 Binary files a/nova/livemigration_test/SI/testParameterSheet_SI.xls and b/nova/livemigration_test/SI/testParameterSheet_SI.xls differ diff --git a/nova/livemigration_test/UT/computeManager.test.py b/nova/livemigration_test/UT/computeManager.test.py index d28d3ccb6..69ee876d1 100644 --- a/nova/livemigration_test/UT/computeManager.test.py +++ b/nova/livemigration_test/UT/computeManager.test.py @@ -1,10 +1,9 @@ #!/usr/bin/python # -*- coding: UTF-8 -*- -NOVA_DIR = '/opt/openstack/nova' -#NOVA_DIR = '/opt/nova-2010.4' import sys +import os import unittest import commands import re @@ -13,6 +12,11 @@ import logging from mock import Mock import twisted +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + try: print print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' \ @@ -40,7 +44,7 @@ try: except: - print 'set PYTHONPATH to nova-install-dir' + print 'set correct NOVA_DIR in this script. ' raise diff --git a/nova/livemigration_test/UT/libvirtConnection.test.py b/nova/livemigration_test/UT/libvirtConnection.test.py index 6a353508d..5dfe8702c 100644 --- a/nova/livemigration_test/UT/libvirtConnection.test.py +++ b/nova/livemigration_test/UT/libvirtConnection.test.py @@ -1,9 +1,9 @@ #!/usr/bin/python # -*- coding: UTF-8 -*- -NOVA_DIR='/opt/nova-2010.4' import sys +import os import unittest import commands import re @@ -13,6 +13,12 @@ import libvirt from mock import Mock import twisted +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + + try : print print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR @@ -40,7 +46,7 @@ try : except: - print 'set PYTHONPATH to nova-install-dir' + print 'set correct NOVA_DIR in this script. ' raise diff --git a/nova/livemigration_test/UT/nova-manage.test.py b/nova/livemigration_test/UT/nova-manage.test.py index dabdba001..f1653d21a 100644 --- a/nova/livemigration_test/UT/nova-manage.test.py +++ b/nova/livemigration_test/UT/nova-manage.test.py @@ -1,15 +1,22 @@ #!/usr/bin/python # -*- coding: UTF-8 -*- -NOVA_DIR='/opt/nova-2010.2' +NOVA_DIR='/opt/nova-2010.4' import sys +import os import unittest import commands import re from mock import Mock +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + + try : print print 'Testing %s/bin/nova-manage, set the NOVA_DIR properly..' % NOVA_DIR @@ -33,7 +40,7 @@ try : except: - print 'set PYTHONPATH to nova-install-dir' + print 'set correct NOVA_DIR in this script. ' raise @@ -42,7 +49,15 @@ class tmpStdout: self.buffer = "" def write(self,arg): self.buffer += arg + def flush(self): + self.buffer = '' + +class tmpStderr(tmpStdout): + def write(self, arg): + self.buffer += arg def flush(self): + pass + def realFlush(self): self.buffer = '' @@ -50,6 +65,8 @@ class NovaManageTestFunctions(unittest.TestCase): stdout = None stdoutBak = None + stderr = None + stderrBak = None hostCmds = None @@ -71,6 +88,12 @@ class NovaManageTestFunctions(unittest.TestCase): self.stdoutBak = sys.stdout sys.stdout = self.stdout + # replace stderr for checking nova-manage output + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + # prepare test data self.setTestData() @@ -164,7 +187,7 @@ class NovaManageTestFunctions(unittest.TestCase): """06: nova-manage host show registerd-host, and no project uses the host""" dic = {'ret': True, - 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, + 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, 'usage': {}} rpc.call = Mock(return_value=dic ) @@ -185,9 +208,9 @@ class NovaManageTestFunctions(unittest.TestCase): and some projects use the host """ dic = {'ret': True, - 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, - 'usage': {'p1': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, - 'p2': {'cpu':1, 'memory_mb':2, 'hdd_gb':3} }} + 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, + 'usage': {'p1': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, + 'p2': {'vcpus':1, 'memory_mb':2, 'local_gb':3} }} rpc.call = Mock(return_value=dic ) self.hostCmds.show('host1') @@ -253,13 +276,12 @@ class NovaManageTestFunctions(unittest.TestCase): """ db.host_get_by_name = Mock(return_value = self.host1) db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) - c1 = c2 = False try : self.instanceCmds.live_migration('i-12345', 'host1') - except SystemExit, e: - c1 = (1 == e.code) - c2 = (0 < self.stdout.buffer.find('is not running') ) - self.assertEqual( c1 and c2 , True ) + except exception.Invalid, e: + c1 = (0 < e.message.find('is not running') ) + self.assertTrue(c1, True) + return False def test14(self): @@ -268,13 +290,12 @@ class NovaManageTestFunctions(unittest.TestCase): """ db.host_get_by_name = Mock(return_value = self.host2) db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) - c1 = c2 = False try : self.instanceCmds.live_migration('i-12345', 'host2') - except SystemExit, e: - c1 = (1 == e.code) - c2 = (0 < self.stdout.buffer.find('is not running') ) - self.assertEqual( c1 and c2 , True ) + except exception.Invalid, e: + c1 = (0 < e.message.find('is not running') ) + self.assertTrue(c1, True) + return False def test15(self): """15: nova-manage instances live_migration ec2_id host, @@ -282,21 +303,31 @@ class NovaManageTestFunctions(unittest.TestCase): """ db.host_get_by_name = Mock(return_value = self.host1) db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - c1 = c2 = False try : self.instanceCmds.live_migration('i-12345', 'host1') - except SystemExit, e: - c1 = (2 == e.code) - c2 = (0 < self.stdout.buffer.find('is running now') ) - self.assertEqual( c1 and c2 , True ) + except exception.Invalid, e: + c1 = ( 0 <= e.message.find('is running now') ) + self.assertTrue(c1, True) + return False + def test16(self): """16: nova-manage instances live_migration ec2_id host, + rpc.call raises RemoteError because destination doesnt have enough resource. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + rpc.call = Mock(return_value = rpc.RemoteError(TypeError, 'val', 'traceback')) + self.assertRaises(rpc.RemoteError, self.instanceCmds.live_migration, 'i-xxx', 'host2' ) + + + def test17(self): + """17: nova-manage instances live_migration ec2_id host, everything goes well, ang gets success messages. """ db.host_get_by_name = Mock(return_value = self.host1) db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - rpc.cast = Mock(return_value = None) + rpc.call = Mock(return_value = None) self.instanceCmds.live_migration('i-12345', 'host2') c1 = (0 <= self.stdout.buffer.find('Finished all procedure') ) @@ -309,6 +340,8 @@ class NovaManageTestFunctions(unittest.TestCase): commands.getstatusoutput('rm -rf %s' % self.getNovaManageCopyPath() ) sys.stdout.flush() sys.stdout = self.stdoutBak + self.stderr.realFlush() + sys.stderr = self.stderrBak if __name__ == '__main__': #unittest.main() diff --git a/nova/livemigration_test/UT/schedulerManager.test.py b/nova/livemigration_test/UT/schedulerManager.test.py index 2fe4d0994..a0b76c918 100644 --- a/nova/livemigration_test/UT/schedulerManager.test.py +++ b/nova/livemigration_test/UT/schedulerManager.test.py @@ -1,15 +1,21 @@ #!/usr/bin/python # -*- coding: UTF-8 -*- -NOVA_DIR='/opt/nova-2010.2' import sys +import os import unittest import commands import re +import libvirt from mock import Mock +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + try : print print 'Checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR @@ -34,7 +40,7 @@ try : from nova.db.sqlalchemy.models import * except: - print 'set PYTHONPATH to nova-install-dir' + print 'set correct NOVA_DIR in this script. ' raise @@ -57,20 +63,25 @@ class SchedulerTestFunctions(unittest.TestCase): self.manager = SchedulerManager(host=self.host) self.setTestData() + self.setMocks() def setTestData(self): self.host1 = Host() self.host1.__setitem__('name', 'host1') - self.host1.__setitem__('cpu', 5) + self.host1.__setitem__('vcpus', 5) self.host1.__setitem__('memory_mb', 20480) - self.host1.__setitem__('hdd_gb', 876) + self.host1.__setitem__('local_gb', 876) self.host2 = Host() self.host2.__setitem__('name', 'host2') - self.host2.__setitem__('cpu', 5) + self.host2.__setitem__('vcpus', 5) self.host2.__setitem__('memory_mb', 20480) - self.host2.__setitem__('hdd_gb', 876) + self.host2.__setitem__('local_gb', 876) + self.host2.__setitem__('hypervisor_type', 'QEMU') + self.host2.__setitem__('hypervisor_version', 12003) + xml="x86_64NehalemIntel" + self.host2.__setitem__('cpu_info', xml) self.instance1 = Instance() for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), @@ -122,7 +133,24 @@ class SchedulerTestFunctions(unittest.TestCase): ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 866) ]: self.instance8.__setitem__(key, val) + self.service1 = Service() + for key, val in [ ('id', 1), ('host', 'host1'), ('binary', 'nova-compute'), + ('topic', 'compute')]: + self.service1.__setitem__(key, val) + + + def setMocks(self): + self.ctxt = context.get_admin_context() + # Mocks for has_enough_resource() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + # Mocks for live_migration + db.instance_get_by_internal_id = Mock(return_value = self.instance1) + # db.host_get_by_name <- defined above. + db.service_get_all_by_topic = Mock(return_value = [self.service1] ) + rpc.call = Mock(return_value=1) def check_format(self, val): """check result format of show_host_resource """ @@ -162,21 +190,21 @@ class SchedulerTestFunctions(unittest.TestCase): sys.stderr.write('return value is not dict') return False - for key in ['cpu', 'memory_mb', 'hdd_gb']: + for key in ['vcpus', 'memory_mb', 'local_gb']: if not val.has_key(key) : sys.stderr.write('invalid format(missing "%s"). ' % key ) return False return True + # ---> test for nova.scheduler.manager.show_host_resource() def test01(self): """01: get NotFound exception when dest host not found on DB """ - ctxt = context.get_admin_context() db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) - result = self.manager.show_host_resource(ctxt, 'not-registered-host') + result = self.manager.show_host_resource(self.ctxt, 'not-registered-host') c1 = ( not result['ret'] ) c2 = ( 0 == result['msg'].find('No such') ) self.assertEqual(c1 and c2, True) @@ -184,21 +212,19 @@ class SchedulerTestFunctions(unittest.TestCase): def test02(self): """02: get other exception if unexpected err. """ - ctxt = context.get_admin_context() db.host_get_by_name = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, self.manager.show_host_resource, ctxt, 'host1' ) + self.assertRaises(TypeError, self.manager.show_host_resource, self.ctxt, 'host1' ) def test03(self): """03: no instance found on dest host. """ - ctxt = context.get_admin_context() db.host_get_by_name = Mock( return_value = self.host1 ) db.instance_get_all_by_host = Mock( return_value=[]) - ret= self.manager.show_host_resource(ctxt, 'host1') + ret= self.manager.show_host_resource(self.ctxt, 'host1') c1 = self.check_format(ret) v = ret['phy_resource'] - c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) + c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) c3 = ( 0 == len(ret['usage']) ) self.assertEqual(c1 and c2 and c3, True) @@ -206,7 +232,6 @@ class SchedulerTestFunctions(unittest.TestCase): def test04(self): """04: some instance found on dest host. """ - ctxt = context.get_admin_context() db.host_get_by_name = Mock( return_value = self.host1 ) db.instance_get_all_by_host = Mock( return_value=[ self.instance1, self.instance2, @@ -216,11 +241,11 @@ class SchedulerTestFunctions(unittest.TestCase): db.instance_get_memory_sum_by_host_and_project = Mock(return_value=1024) db.instance_get_disk_sum_by_host_and_project = Mock(return_value=5) - ret= self.manager.show_host_resource(ctxt, 'host1') + ret= self.manager.show_host_resource(self.ctxt, 'host1') c1 = self.check_format(ret) v = ret['phy_resource'] - c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) + c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) c3 = ( 2 == len(ret['usage']) ) c4 = ( self.instance1['project_id'] in ret['usage'].keys()) c5 = ( self.instance3['project_id'] in ret['usage'].keys()) @@ -232,58 +257,51 @@ class SchedulerTestFunctions(unittest.TestCase): def test05(self): """05: when cpu is exccded some instance found on dest host. """ - ctxt = context.get_admin_context() db.instance_get = Mock(return_value = self.instance6) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + try : + self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 < e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) def test06(self): """06: when memory is exccded some instance found on dest host. """ - ctxt = context.get_admin_context() db.instance_get = Mock(return_value = self.instance7) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) + try : + self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 <= e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False def test07(self): """07: when hdd is exccded some instance found on dest host. """ - ctxt = context.get_admin_context() db.instance_get = Mock(return_value = self.instance8) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + try : + self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 <= e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) def test08(self): """08: everything goes well. (instance_get_all_by_host returns list)""" - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, True) + ret= self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + self.assertEqual(ret, None) def test09(self): """09: everything goes well(instance_get_all_by_host returns[]). """ - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) db.instance_get_all_by_host = Mock(return_value = [] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, True) + ret= self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + self.assertEqual(ret, None) # ---> test for nova.scheduler.manager.live_migration() @@ -291,60 +309,90 @@ class SchedulerTestFunctions(unittest.TestCase): def test10(self): """10: instance_get_by_internal_id issue NotFound. """ + # Mocks for has_enough_resource() - ctxt = context.get_admin_context() db.instance_get = Mock(return_value = self.instance8) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - # Mocks for live_migration()db.instance_get_by_internal_id # (any Mock is ok here. important mock is all above) db.instance_get_by_internal_id = Mock(side_effect=exception.NotFound("ERR")) self.assertRaises(exception.NotFound, self.manager.live_migration, - ctxt, + self.ctxt, 'i-12345', 'host1') def test11(self): - """11: return False if host doesnt have enough resource. """ + """11: get NotFound exception when dest host not found on DB """ + + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'i-12345', + 'host1') + + + def test12(self): + """12: Destination host is not compute node """ + self.assertRaises(exception.Invalid, + self.manager.live_migration, + self.ctxt, + 'i-12345', + 'host2') + + + # Cannot test the case of hypervisor type difference and hypervisor + # version difference, since we cannot set different mocks to same method.. + + def test13(self): + """13: rpc.call raises RemoteError(Unexpected error occurs when executing compareCPU) """ + rpc.call = Mock(return_value = rpc.RemoteError(libvirt.libvirtError, 'val', 'traceback')) + self.assertRaises(rpc.RemoteError, + self.manager.live_migration, + self.ctxt, + 'i-12345', + 'host1') + + def test14(self): + """14: rpc.call returns 0 (cpu is not compatible between src and dest) """ + rpc.call = Mock(return_value = 0) + try : + self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + except exception.Invalid, e: + c1 = ( 0 <= e.message.find('doesnt have compatibility to')) + self.assertTrue(c1, True) + return False + + def test15(self): + """15: raise NotEmpty if host doesnt have enough resource. """ # Mocks for has_enough_resource() - ctxt = context.get_admin_context() db.instance_get = Mock(return_value = self.instance8) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - # Mocks for live_migration()db.instance_get_by_internal_id - # (any Mock is ok here. important mock is all above) + # Mocks for live_migration() db.instance_get_by_internal_id = Mock(return_value = self.instance8) db.instance_set_state = Mock(return_value = True) rpc_cast = Mock(return_value = True) - ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) - - + try : + self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 <= e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False - def test12(self): - """12: everything goes well. """ - # Mocks for has_enough_resource() - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + def test16(self): + """16: everything goes well. """ - # Mocks for live_migration()db.instance_get_by_internal_id - # (any Mock is ok here. important mock is all above) db.instance_get_by_internal_id = Mock(return_value = self.instance8) db.instance_set_state = Mock(return_value = True) rpc.cast = Mock(return_value = True) - ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, True) + ret= self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + self.assertEqual(ret, None) def tearDown(self): diff --git a/nova/livemigration_test/UT/testCase_UT.xls b/nova/livemigration_test/UT/testCase_UT.xls index 0524526b6..f73e8c5aa 100644 Binary files a/nova/livemigration_test/UT/testCase_UT.xls and b/nova/livemigration_test/UT/testCase_UT.xls differ diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index d36525506..0921e3791 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -73,29 +73,79 @@ class SchedulerManager(manager.Manager): def live_migration(self, context, ec2_id, dest): """ live migration method""" + # (masumotok) below pre-checking is followed by + # http://wiki.libvirt.org/page/TodoPreMigrationChecks + # 1. get instance id internal_id = cloud.ec2_id_to_internal_id(ec2_id) instance_ref = db.instance_get_by_internal_id(context, internal_id) instance_id = instance_ref['id'] - # 2. check dst host still has enough capacities - if not self.has_enough_resource(context, instance_id, dest): - return False - - # 3. change instance_state + # 2. get src host and dst host + src = instance_ref['launch_at'] + shost_ref = db.host_get_by_name(context, src ) + dhost_ref = db.host_get_by_name(context, dest) + + # 3. dest should be compute + services = db.service_get_all_by_topic(context, 'compute') + logging.warn('%s' % [service.host for service in services]) + if dest not in [service.host for service in services] : + raise exception.Invalid('%s must be compute node' % dest) + + # 4. check hypervisor is same + shypervisor = shost_ref['hypervisor_type'] + dhypervisor = dhost_ref['hypervisor_type'] + if shypervisor != dhypervisor: + msg = 'Different hypervisor type(%s->%s)' % (shypervisor, dhypervisor) + raise exception.Invalid(msg) + + # 5. check hypervisor version + shypervisor = shost_ref['hypervisor_version'] + dhypervisor = dhost_ref['hypervisor_version'] + if shypervisor > dhypervisor: + msg = 'Older hypervisor version(%s->%s)' % (shypervisor, dhypervisor) + raise exception.Invalid(msg) + + # 6. check cpuinfo + cpuinfo = shost_ref['cpu_info'] + if str != type(cpuinfo): + msg = 'Unexpected err: no cpu_info for %s found on DB.hosts' % src + raise exception.Invalid(msg) + + logging.warn('cpuinfo %s %d' % (cpuinfo, len(cpuinfo))) + ret = rpc.call(context, + db.queue_get_for(context, FLAGS.compute_topic, dest), + {"method": 'compareCPU', + "args": {'xml': cpuinfo}}) + + if int != type(ret): + raise ret + + if 0 >= ret : + msg = '%s doesnt have compatibility to %s(where %s launching at)\n' \ + % (dest, src, ec2_id) + msg += 'result:%d \n' % ret + msg += 'Refer to %s' % \ + 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult' + raise exception.Invalid(msg) + + # 7. check dst host still has enough capacities + self.has_enough_resource(context, instance_id, dest) + + # 8. change instance_state db.instance_set_state(context, instance_id, power_state.PAUSED, 'migrating') - # 4. request live migration + # 9. request live migration host = instance_ref['host'] rpc.cast(context, db.queue_get_for(context, FLAGS.compute_topic, host), {"method": 'live_migration', "args": {'instance_id': instance_id, 'dest': dest}}) - return True + def has_enough_resource(self, context, instance_id, dest): """ check if destination host has enough resource for live migration""" @@ -126,12 +176,10 @@ class SchedulerManager(manager.Manager): (ec2_id, total_cpu, total_mem, total_hdd)) if total_cpu <= vcpus or total_mem <= mem or total_hdd <= hdd: - logging.debug('%s doesnt have enough resource for %s' % - (dest, ec2_id)) - return False + msg = '%s doesnt have enough resource for %s' % (dest, ec2_id) + raise exception.NotEmpty(msg) logging.debug('%s has enough resource for %s' % (dest, ec2_id)) - return True def show_host_resource(self, context, host, *args): """ show the physical/usage resource given by hosts.""" diff --git a/nova/service.py b/nova/service.py index 3ce07a3e0..416448faa 100644 --- a/nova/service.py +++ b/nova/service.py @@ -119,14 +119,21 @@ class Service(object, service.Service): def _update_host_ref(self, context, host_ref): if 0 <= self.manager_class_name.find('ComputeManager'): - cpu = self.manager.get_vcpu_number() - memory_mb = self.manager.get_mem_size() - local_gb = self.manager.get_hdd_size() + vcpu = self.manager.driver.get_vcpu_number() + memory_mb = self.manager.get_memory_mb() + local_gb = self.manager.get_local_gb() + hypervisor = self.manager.driver.get_hypervisor_type() + version = self.manager.driver.get_hypervisor_version() + cpu_xml = self.manager.driver.get_cpu_xml() + db.host_update(context, host_ref['id'], - {'vcpus': cpu, + {'vcpus': vcpu, 'memory_mb': memory_mb, - 'local_gb': local_gb}) + 'local_gb': local_gb, + 'hypervisor_type': hypervisor, + 'hypervisor_version': version, + 'cpu_info':cpu_xml }) return host_ref def __getattr__(self, key): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f2b5cf794..6450db8bd 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -44,6 +44,7 @@ Supports KVM, QEMU, UML, and XEN. import logging import os import shutil +import re import IPy from twisted.internet import defer @@ -632,9 +633,30 @@ class LibvirtConnection(object): return interfaces def get_vcpu_number(self): - """ get vcpu number of physical computer """ + """ Get vcpu number of physical computer. """ return self._conn.getMaxVcpus(None) + def get_hypervisor_type(self): + """ Get hypervisor type """ + return self._conn.getType() + + def get_hypervisor_version(self): + """ Get hypervisor version """ + return self._conn.getVersion() + + def get_cpu_xml(self): + """ Get cpuinfo information """ + xmlstr = self._conn.getCapabilities() + xml = libxml2.parseDoc(xmlstr) + nodes = xml.xpathEval('//cpu') + if 1 != len(nodes): + msg = 'Unexpected xml format. tag "cpu" must be 1, but %d.' % len(nodes) + msg += '\n'+xml.serialize() + raise exception.Invalid(msg) + cpuxmlstr = re.sub("\n|[ ]+", ' ', nodes[0].serialize()) + return cpuxmlstr + + def block_stats(self, instance_name, disk): """ Note that this function takes an instance name, not an Instance, so @@ -651,14 +673,17 @@ class LibvirtConnection(object): domain = self._conn.lookupByName(instance_name) return domain.interfaceStats(interface) + def refresh_security_group(self, security_group_id): fw = NWFilterFirewall(self._conn) fw.ensure_security_group_filter(security_group_id) + def setup_nwfilters_for_instance(self, instance): nwfilter = NWFilterFirewall(self._conn) return nwfilter.setup_nwfilters_for_instance(instance) + def nwfilter_for_instance_exists(self, instance_ref): try: filter = 'nova-instance-%s' % instance_ref.name @@ -667,6 +692,19 @@ class LibvirtConnection(object): except libvirt.libvirtError: return False + + def compareCPU(self, xml): + """ + Check the host cpu is compatible to a cpu given by xml. + "xml" must be a part of libvirt.openReadonly().getCapabilities(). + return values follows by virCPUCompareResult. + if 0 > return value, do live migration. + + 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult' + """ + return self._conn.compareCPU(xml,0) + + def live_migration(self, instance_ref, dest): uri = FLAGS.live_migration_uri % dest out, err = utils.execute("sudo virsh migrate --live %s %s" -- cgit From 6b4511d33562da46f9954bf5423ada49f9696d75 Mon Sep 17 00:00:00 2001 From: masumotok Date: Fri, 24 Dec 2010 15:45:05 +0900 Subject: テスト項目表がなぜか消えたので追加 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/livemigration_test/SI/testCase_SI.xls | Bin 0 -> 43520 bytes nova/livemigration_test/SI/testParameterSheet_SI.xls | Bin 0 -> 464384 bytes nova/livemigration_test/UT/testCase_UT.xls | Bin 0 -> 202752 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 nova/livemigration_test/SI/testCase_SI.xls create mode 100644 nova/livemigration_test/SI/testParameterSheet_SI.xls create mode 100644 nova/livemigration_test/UT/testCase_UT.xls (limited to 'nova') diff --git a/nova/livemigration_test/SI/testCase_SI.xls b/nova/livemigration_test/SI/testCase_SI.xls new file mode 100644 index 000000000..65cf96fd7 Binary files /dev/null and b/nova/livemigration_test/SI/testCase_SI.xls differ diff --git a/nova/livemigration_test/SI/testParameterSheet_SI.xls b/nova/livemigration_test/SI/testParameterSheet_SI.xls new file mode 100644 index 000000000..400b43b43 Binary files /dev/null and b/nova/livemigration_test/SI/testParameterSheet_SI.xls differ diff --git a/nova/livemigration_test/UT/testCase_UT.xls b/nova/livemigration_test/UT/testCase_UT.xls new file mode 100644 index 000000000..f73e8c5aa Binary files /dev/null and b/nova/livemigration_test/UT/testCase_UT.xls differ -- cgit From 21c1ba77cdb95b95a13a81c243ac13e0cf8a632f Mon Sep 17 00:00:00 2001 From: masumotok Date: Fri, 24 Dec 2010 16:05:24 +0900 Subject: テスト項目表をローカルから一度削除した状態でコミット MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/livemigration_test/SI/picture.pptx | Bin 137730 -> 0 bytes nova/livemigration_test/SI/testCase_SI.xls | Bin 43520 -> 0 bytes .../SI/testParameterSheet_SI.xls | Bin 464384 -> 0 bytes nova/livemigration_test/SI/utils/demo-firstboot.sh | 39 - .../SI/utils/demo-runInstance.sh | 57 -- nova/livemigration_test/SI/utils/nova-manage.conf | 18 - nova/livemigration_test/SI/utils/nova.conf | 10 - nova/livemigration_test/SI/utils/nova.sh | 180 ----- nova/livemigration_test/SI/utils/nova.sh.compute | 37 - nova/livemigration_test/UT/computeManager.test.py | 821 --------------------- .../UT/libvirtConnection.test.py | 741 ------------------- nova/livemigration_test/UT/nova-manage.test.py | 672 ----------------- .../livemigration_test/UT/schedulerManager.test.py | 771 ------------------- nova/livemigration_test/UT/testCase_UT.xls | Bin 202752 -> 0 bytes 14 files changed, 3346 deletions(-) delete mode 100644 nova/livemigration_test/SI/picture.pptx delete mode 100644 nova/livemigration_test/SI/testCase_SI.xls delete mode 100644 nova/livemigration_test/SI/testParameterSheet_SI.xls delete mode 100755 nova/livemigration_test/SI/utils/demo-firstboot.sh delete mode 100755 nova/livemigration_test/SI/utils/demo-runInstance.sh delete mode 100644 nova/livemigration_test/SI/utils/nova-manage.conf delete mode 100644 nova/livemigration_test/SI/utils/nova.conf delete mode 100755 nova/livemigration_test/SI/utils/nova.sh delete mode 100755 nova/livemigration_test/SI/utils/nova.sh.compute delete mode 100644 nova/livemigration_test/UT/computeManager.test.py delete mode 100644 nova/livemigration_test/UT/libvirtConnection.test.py delete mode 100644 nova/livemigration_test/UT/nova-manage.test.py delete mode 100644 nova/livemigration_test/UT/schedulerManager.test.py delete mode 100644 nova/livemigration_test/UT/testCase_UT.xls (limited to 'nova') diff --git a/nova/livemigration_test/SI/picture.pptx b/nova/livemigration_test/SI/picture.pptx deleted file mode 100644 index b47bec9b5..000000000 Binary files a/nova/livemigration_test/SI/picture.pptx and /dev/null differ diff --git a/nova/livemigration_test/SI/testCase_SI.xls b/nova/livemigration_test/SI/testCase_SI.xls deleted file mode 100644 index 65cf96fd7..000000000 Binary files a/nova/livemigration_test/SI/testCase_SI.xls and /dev/null differ diff --git a/nova/livemigration_test/SI/testParameterSheet_SI.xls b/nova/livemigration_test/SI/testParameterSheet_SI.xls deleted file mode 100644 index 400b43b43..000000000 Binary files a/nova/livemigration_test/SI/testParameterSheet_SI.xls and /dev/null differ diff --git a/nova/livemigration_test/SI/utils/demo-firstboot.sh b/nova/livemigration_test/SI/utils/demo-firstboot.sh deleted file mode 100755 index 3a6f7fb0b..000000000 --- a/nova/livemigration_test/SI/utils/demo-firstboot.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -DIR=/opt/nova-2010.1 - -# 1. 管理者ユーザを作成する -# nova-manage user admin ユーザ名 access-key secret-key -# -#$DIR/bin/nova-manage user admin admin admin admin - -# 2. プロジェクトを作成する -# nova-manage create project プロジェクト名 プロジェクトに属するユーザ名 -# -#$DIR/bin/nova-manage project create admin admin - -# 3. クラウドを使うための認証情報を生成する -# nova-manage project environment プロジェクト名 ユーザ名 認証情報を格納するファイル -# -#$DIR/bin/nova-manage project environment admin admin $DIR/novarc - -# 4. 認証情報の読み込み -. $DIR/novarc - -# 5. プロジェクト用仮想マシンネットワークの作成を行う -# nova-manage user admin ユーザ名 access-key secret-key -# -$DIR/bin/nova-manage network create 10.0.0.0/8 3 16 - -# 6. 初回ログインにはSSHの公開鍵認証が必要 -# -if [ "" == "`euca-describe-keypairs | grep testkey`" ]; then - euca-add-keypair testkey > testkey.pem -fi - -# 7. -for i in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do - sudo ip addr del $i dev eth0 2> /dev/null -done - - diff --git a/nova/livemigration_test/SI/utils/demo-runInstance.sh b/nova/livemigration_test/SI/utils/demo-runInstance.sh deleted file mode 100755 index 171291262..000000000 --- a/nova/livemigration_test/SI/utils/demo-runInstance.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -DIR=/opt/nova-2010.1 - -function inc_assigned(){ - assigned=`expr $assigned + 1` -} - - -# 1. 認証情報の読み込み -. $DIR/novarc - -# 3. 仮想マシンの起動 -# -ret=`euca-run-instances -t m1.small -k testkey ami-centos` -#ret=`euca-run-instances -t m1.small -k testkey ami-tiny` - -# 4. 仮想マシン用IPの確保 -# 未登録なら登録しておく -registered=`euca-describe-addresses` -for ip in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do - - not_registered=`echo $registered | grep $ip` - if [ "" == "$not_registered" ]; then - echo "[INFO] registed $ip" - $DIR/bin/nova-manage floating create `hostname` $ip - fi -done - -# 5. IPの割当 -echo 0 > /tmp/demo-runinstance -euca-describe-addresses | grep -v reserved | while read line; do - # 割り当てられてないものを仮想マシンに割り当てる - ip=`echo $line | cut -d ' ' -f 2` - id=`echo $ret | cut -d ' ' -f 5` - if [ "" == "`echo $id | grep i- `" ] ; then - echo "[INFO] try again" $ret - break - fi - echo "[INFO] assigned to ipaddr($ip) to instance($id) " - euca-associate-address -i $id $ip - echo 1 > /tmp/demo-runinstance - break -done - -echo $assigned -if [ 0 -eq "`cat /tmp/demo-runinstance`" ] ; then - echo "[INFO] address is full." -fi -rm -rf /tmp/demo-runinstance - - -# 6. FWの設定 -euca-authorize -P tcp -p 22 default 2> /dev/null > /dev/null -euca-authorize -P tcp -p 80 default 2> /dev/null > /dev/null -euca-authorize -P tcp -p 5555 default 2> /dev/null > /dev/null - diff --git a/nova/livemigration_test/SI/utils/nova-manage.conf b/nova/livemigration_test/SI/utils/nova-manage.conf deleted file mode 100644 index 9f8a02b96..000000000 --- a/nova/livemigration_test/SI/utils/nova-manage.conf +++ /dev/null @@ -1,18 +0,0 @@ ---verbose ---nodaemon ---dhcpbridge_flagfile=/etc/nova/nova-manage.conf ---FAKE_subdomain=ec2 ---libvirt_type=qemu ---no_internet_conn=True ---public_netif=eth0 ---public_interface=eth0 - ---cc-host=172.19.0.131 ---routing_source_ip=172.19.0.131 ---sql_connection=mysql://root:nova@172.19.0.131/nova ---rabbit_host=172.19.0.131 ---redis_host=172.19.0.131 ---s3_host=172.19.0.131 ---auth_driver=nova.auth.ldapdriver.LdapDriver ---ldap_url=ldap://172.19.0.131 - diff --git a/nova/livemigration_test/SI/utils/nova.conf b/nova/livemigration_test/SI/utils/nova.conf deleted file mode 100644 index c66bfbc53..000000000 --- a/nova/livemigration_test/SI/utils/nova.conf +++ /dev/null @@ -1,10 +0,0 @@ ---verbose ---nodaemon ---dhcpbridge_flagfile=/opt/nova-2010.4//bin/nova.conf ---network_manager=nova.network.manager.VlanManager ---cc_host=172.19.0.131 ---routing_source_ip=172.19.0.131 ---sql_connection=mysql://root:nova@localhost/nova ---auth_driver=nova.auth.ldapdriver.LdapDriver ---libvirt_type=qemu ---public_interface=eth0 diff --git a/nova/livemigration_test/SI/utils/nova.sh b/nova/livemigration_test/SI/utils/nova.sh deleted file mode 100755 index b8e2e9f26..000000000 --- a/nova/livemigration_test/SI/utils/nova.sh +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env bash -DIR=`pwd` -CMD=$1 -SOURCE_BRANCH=lp:nova -if [ -n "$2" ]; then - SOURCE_BRANCH=$2 -fi -#DIRNAME=nova -DIRNAME="" -NOVA_DIR=$DIR/$DIRNAME -if [ -n "$3" ]; then - NOVA_DIR=$DIR/$3 -fi - -if [ ! -n "$HOST_IP" ]; then - # NOTE(vish): This will just get the first ip in the list, so if you - # have more than one eth device set up, this will fail, and - # you should explicitly set HOST_IP in your environment - HOST_IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` -fi - -#USE_MYSQL=${USE_MYSQL:-0} -USE_MYSQL=1 -MYSQL_PASS=${MYSQL_PASS:-nova} -TEST=${TEST:-0} -#USE_LDAP=${USE_LDAP:-0} -USE_LDAP=1 -LIBVIRT_TYPE=${LIBVIRT_TYPE:-qemu} -NET_MAN=${NET_MAN:-VlanManager} -# NOTE(vish): If you are using FlatDHCP on multiple hosts, set the interface -# below but make sure that the interface doesn't already have an -# ip or you risk breaking things. -# FLAT_INTERFACE=eth0 - -if [ "$USE_MYSQL" == 1 ]; then - SQL_CONN=mysql://root:$MYSQL_PASS@localhost/nova -else - SQL_CONN=sqlite:///$NOVA_DIR/nova.sqlite -fi - -if [ "$USE_LDAP" == 1 ]; then - AUTH=ldapdriver.LdapDriver -else - AUTH=dbdriver.DbDriver -fi - -mkdir -p /etc/nova -cat >$NOVA_DIR/bin/nova.conf << NOVA_CONF_EOF ---verbose ---nodaemon ---dhcpbridge_flagfile=$NOVA_DIR/bin/nova.conf ---network_manager=nova.network.manager.$NET_MAN ---cc_host=$HOST_IP ---routing_source_ip=$HOST_IP ---sql_connection=$SQL_CONN ---auth_driver=nova.auth.$AUTH ---libvirt_type=$LIBVIRT_TYPE ---public_interface=eth0 -NOVA_CONF_EOF - -if [ -n "$FLAT_INTERFACE" ]; then - echo "--flat_interface=$FLAT_INTERFACE" >>$NOVA_DIR/bin/nova.conf -fi - -if [ "$CMD" == "branch" ]; then - sudo apt-get install -y bzr - rm -rf $NOVA_DIR - bzr branch $SOURCE_BRANCH $NOVA_DIR - cd $NOVA_DIR - mkdir -p $NOVA_DIR/instances - mkdir -p $NOVA_DIR/networks -fi - -# You should only have to run this once -if [ "$CMD" == "install" ]; then - sudo apt-get install -y python-software-properties - sudo add-apt-repository ppa:nova-core/ppa - sudo apt-get update - sudo apt-get install -y dnsmasq kpartx kvm gawk iptables ebtables - sudo apt-get install -y user-mode-linux kvm libvirt-bin - sudo apt-get install -y screen euca2ools vlan curl rabbitmq-server - sudo apt-get install -y lvm2 iscsitarget open-iscsi - echo "ISCSITARGET_ENABLE=true" | sudo tee /etc/default/iscsitarget - sudo /etc/init.d/iscsitarget restart - sudo modprobe kvm - sudo /etc/init.d/libvirt-bin restart - sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot - sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy - sudo apt-get install -y python-libvirt python-libxml2 python-routes - if [ "$USE_MYSQL" == 1 ]; then - cat </etc/nova/nova-manage.conf << NOVA_CONF_EOF ---verbose ---nodaemon ---dhcpbridge_flagfile=/etc/nova/nova-manage.conf ---FAKE_subdomain=ec2 ---libvirt_type=qemu ---no_internet_conn=True ---public_netif=eth0 ---public_interface=eth0 - ---cc-host=$HOST_IP ---routing_source_ip=$HOST_IP ---sql_connection=mysql://root:nova@$HOST_IP/nova ---rabbit_host=$HOST_IP ---redis_host=$HOST_IP ---s3_host=$HOST_IP ---auth_driver=nova.auth.ldapdriver.LdapDriver ---ldap_url=ldap://$HOST_IP - -NOVA_CONF_EOF - -$DIR/bin/nova-compute --flagfile=/etc/nova/nova-manage.conf - diff --git a/nova/livemigration_test/UT/computeManager.test.py b/nova/livemigration_test/UT/computeManager.test.py deleted file mode 100644 index bc1091299..000000000 --- a/nova/livemigration_test/UT/computeManager.test.py +++ /dev/null @@ -1,821 +0,0 @@ -<<<<<<< TREE -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -NOVA_DIR = '/opt/openstack/nova' -#NOVA_DIR = '/opt/nova-2010.4' - -import sys -import unittest -import commands -import re -import logging - -from mock import Mock -import twisted - -try: - print - print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' \ - % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.compute.manager import ComputeManager - from nova.virt.libvirt_conn import LibvirtConnection - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set PYTHONPATH to nova-install-dir' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - - def write(self, arg): - self.buffer += arg - - def writelines(self, arg): - self.buffer += arg - - def flush(self): - print 'flush' - self.buffer = '' - - -class tmpStderr(tmpStdout): - def write(self, arg): - self.buffer += arg - - def flush(self): - pass - - def realFlush(self): - self.buffer = '' - -dummyCallReturnValue={ 0:True } -dummyCallCount=0 -def dummyCall(context, topic, method): - global dummyCallReturnValue, dummyCallCount - if dummyCallCount in dummyCallReturnValue.keys() : - ret = dummyCallReturnValue[ dummyCallCount ] - dummyCallCount += 1 - return ret - else : - dummyCallCount += 1 - return False - - -class ComputeTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - manager = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - #if self.stdout is None: - # self.__class__.stdout = tmpStdout() - #self.stdoutBak = sys.stdout - #sys.stdout = self.stdout - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - self.host = 'openstack2-api' - if self.manager is None: - self.__class__.manager = ComputeManager(host=self.host) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - for key, val in [('name', 'host1'), ('cpu', 5), - ('memory_mb', 20480), ('hdd_gb', 876)]: - self.host1.__setitem__(key, val) - - self.host2 = Host() - for key, val in [('name', 'host2'), ('cpu', 5), - ('memory_mb', 20480), ('hdd_gb', 876)]: - self.host2.__setitem__(key, val) - - self.instance1 = Instance() - for key, val in [('id', 1), ('host', 'host1'), - ('hostname', 'i-12345'), ('state', power_state.RUNNING), - ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), - ('hdd_gb', 5), ('internal_id', 12345)]: - self.instance1.__setitem__(key, val) - - self.instance2 = Instance() - for key, val in [('id', 2), ('host', 'host1'), - ('hostname', 'i-12345'), ('state', power_state.RUNNING), - ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), - ('hdd_gb', 5)]: - self.instance2.__setitem__(key, val) - - self.fixed_ip1 = FixedIp() - for key, val in [('id', 1), ('address', '1.1.1.1'), - ('network_id', '1'), ('instance_id', 1)]: - self.fixed_ip1.__setitem__(key, val) - - self.vol1 = Volume() - for key, val in [('id', 1), ('ec2_id', 'vol-qijjuc7e'), - ('availability_zone', 'nova'), ('host', 'host1')]: - self.vol1.__setitem__(key, val) - - self.vol2 = Volume() - for key, val in [('id', 2), ('ec2_id', 'vol-qi22222'), - ('availability_zone', 'nova'), ('host', 'host1')]: - self.vol2.__setitem__(key, val) - - self.secgrp1 = Volume() - for key, val in [('id', 1), ('ec2_id', 'default')]: - self.secgrp1.__setitem__(key, val) - - self.secgrp2 = Volume() - for key, val in [('id', 2), ('ec2_id', 'def2')]: - self.secgrp2.__setitem__(key, val) - - self.netref1 = Network() - - def setMocks(self): - - # mocks for pre_live_migration - self.ctxt = context.get_admin_context() - db.instance_get = Mock(return_value=self.instance1) - db.volume_get_by_ec2_id = Mock(return_value=[self.vol1, self.vol2]) - db.volume_get_shelf_and_blade = Mock(return_value=(3, 4)) - db.instance_get_fixed_address = Mock(return_value=self.fixed_ip1) - db.security_group_get_by_instance \ - = Mock(return_value=[self.secgrp1, self.secgrp2]) - self.manager.driver.setup_nwfilters_for_instance \ - = Mock(return_value=None) - self.manager.driver.nwfilter_for_instance_exists = Mock(return_value=None) - self.manager.network_manager.setup_compute_network \ - = Mock(return_value=None) - # mocks for live_migration_ - rpc.call = Mock(return_value=True) - db.instance_set_state = Mock(return_value=True) - - # ---> test for nova.compute.manager.pre_live_migration() - def test01(self): - """01: NotFound error occurs on finding instance on DB. """ - - db.instance_get = Mock(side_effect=exception.NotFound('ERR')) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test02(self): - """02: NotAuthrized occurs on finding volume on DB. """ - - db.volume_get_by_ec2_id \ - = Mock(side_effect=exception.NotAuthorized('ERR')) - - self.assertRaises(exception.NotAuthorized, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test03(self): - """03: Unexpected exception occurs on finding volume on DB. """ - - db.volume_get_by_ec2_id = Mock(side_effect=TypeError('ERR')) - - self.assertRaises(TypeError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test04(self): - """04: no volume and fixed ip found on DB, """ - - db.volume_get_by_ec2_id = Mock(side_effect=exception.NotFound('ERR')) - db.instance_get_fixed_address = Mock(return_value=None) - - self.assertRaises(rpc.RemoteError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - c1 = (0 <= sys.stderr.buffer.find('has no volume')) - - self.assertEqual(c1, True) - - def test05(self): - """05: volume found and no fixed_ip found on DB. """ - - db.instance_get_fixed_address \ - = Mock(side_effect=exception.NotFound('ERR')) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test06(self): - """06: self.driver.setup_nwfilters_for_instance causes NotFound. """ - self.manager.driver.setup_nwfilters_for_instance \ - = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test07(self): - """07: self.network_manager.setup_compute_network causes ProcessExecutionError. """ - self.manager.network_manager.setup_compute_network \ - = Mock(side_effect=exception.ProcessExecutionError("ERR")) - - self.assertRaises(exception.ProcessExecutionError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - - def test08(self): - """08: self.manager.network_manager.setup_compute_network - exception.NotFound. """ - self.manager.network_manager.setup_compute_network \ - = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - # those 2 cases are omitted : - # self.driver.setup_nwfilters_for_instance causes - # twisted.python.failure.Failure. - # self.driver.refresh_security_group causes twisted.python.failure.Failure. - # - # twisted.python.failure.Failure can not be used with assertRaises, - # it doesnt have __call___ - # - - def test09(self): - """09: volume/fixed_ip found on DB, all procedure finish - successfully.. """ - - result = self.manager.pre_live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - self.assertEqual(result, True) - - # ---> test for nova.compute.manager.live_migration() - - def test10(self): - """10: rpc.call(pre_live_migration returns Error(Not None). """ - rpc.call = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test11(self): - """11: if rpc.call returns rpc.RemoteError. """ - - rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) - db.instance_set_state = Mock(return_value=True) - result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - c1 = (None == result) - c2 = (0 <= sys.stderr.buffer.find('err at')) - self.assertEqual(c1 and c2, True) - - def test12(self): - """12: if rpc.call returns rpc.RemoteError and instance_set_state - also ends up err. (then , unexpected err occurs, in this case - TypeError) - """ - rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) - db.instance_set_state = Mock(side_effect=TypeError("ERR")) - self.assertRaises(TypeError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test13(self): - """13: if wait for pre_live_migration, but timeout. """ - rpc.call = dummyCall - - db.instance_get = Mock(return_value=self.instance1) - - result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - c1 = (None == result) - c2 = (0 <= sys.stderr.buffer.find('Timeout for')) - self.assertEqual(c1 and c2, True) - - def test14(self): - """14: if db_instance_get issues NotFound. - """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(side_effect=exception.NotFound("ERR")) - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test15(self): - """15: if rpc.call returns True, and instance_get() cause other - exception. (Unexpected case - b/c it already checked by - nova-manage) - """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(side_effect=TypeError("ERR")) - - self.assertRaises(TypeError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test16(self): - """16: if rpc.call returns True, and live_migration issues - ProcessExecutionError. """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(return_value=self.instance1) - ret = self.manager.driver.live_migration \ - = Mock(side_effect=utils.ProcessExecutionError("ERR")) - - self.assertRaises(utils.ProcessExecutionError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test17(self): - """17: everything goes well. """ - self.manager.driver.live_migration = Mock(return_value=True) - ret = self.manager.live_migration(self.ctxt, 'i-12345', 'host1') - self.assertEqual(True, True) - - def tearDown(self): - """common terminating method. """ - self.stderr.realFlush() - sys.stderr = self.stderrBak - #sys.stdout = self.stdoutBak - -if __name__ == '__main__': - logging.getLogger().setLevel(logging.DEBUG) - #unittest.main() - - suite = unittest.TestLoader().loadTestsFromTestCase(ComputeTestFunctions) - unittest.TextTestRunner(verbosity=2).run(suite) - - #suite = unittest.TestSuite() - #suite.addTest(ComputeTestFunctions("test15")) - #suite.addTest(ComputeTestFunctions("test16")) - #unittest.TextTestRunner(verbosity=2).run(suite) -======= -#!/usr/bin/python -# -*- coding: UTF-8 -*- - - -import sys -import os -import unittest -import commands -import re -import logging - -from mock import Mock -import twisted - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - -try: - print - print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' \ - % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.compute.manager import ComputeManager - from nova.virt.libvirt_conn import LibvirtConnection - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - - def write(self, arg): - self.buffer += arg - - def writelines(self, arg): - self.buffer += arg - - def flush(self): - print 'flush' - self.buffer = '' - - -class tmpStderr(tmpStdout): - def write(self, arg): - self.buffer += arg - - def flush(self): - pass - - def realFlush(self): - self.buffer = '' - -dummyCallReturnValue={ 0:True } -dummyCallCount=0 -def dummyCall(context, topic, method): - global dummyCallReturnValue, dummyCallCount - if dummyCallCount in dummyCallReturnValue.keys() : - ret = dummyCallReturnValue[ dummyCallCount ] - dummyCallCount += 1 - return ret - else : - dummyCallCount += 1 - return False - - -class ComputeTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - manager = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - #if self.stdout is None: - # self.__class__.stdout = tmpStdout() - #self.stdoutBak = sys.stdout - #sys.stdout = self.stdout - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - self.host = 'openstack2-api' - if self.manager is None: - self.__class__.manager = ComputeManager(host=self.host) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - for key, val in [('name', 'host1'), ('cpu', 5), - ('memory_mb', 20480), ('hdd_gb', 876)]: - self.host1.__setitem__(key, val) - - self.host2 = Host() - for key, val in [('name', 'host2'), ('cpu', 5), - ('memory_mb', 20480), ('hdd_gb', 876)]: - self.host2.__setitem__(key, val) - - self.instance1 = Instance() - for key, val in [('id', 1), ('host', 'host1'), - ('hostname', 'i-12345'), ('state', power_state.RUNNING), - ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), - ('hdd_gb', 5), ('internal_id', 12345)]: - self.instance1.__setitem__(key, val) - - self.instance2 = Instance() - for key, val in [('id', 2), ('host', 'host1'), - ('hostname', 'i-12345'), ('state', power_state.RUNNING), - ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), - ('hdd_gb', 5)]: - self.instance2.__setitem__(key, val) - - self.fixed_ip1 = FixedIp() - for key, val in [('id', 1), ('address', '1.1.1.1'), - ('network_id', '1'), ('instance_id', 1)]: - self.fixed_ip1.__setitem__(key, val) - - self.vol1 = Volume() - for key, val in [('id', 1), ('ec2_id', 'vol-qijjuc7e'), - ('availability_zone', 'nova'), ('host', 'host1')]: - self.vol1.__setitem__(key, val) - - self.vol2 = Volume() - for key, val in [('id', 2), ('ec2_id', 'vol-qi22222'), - ('availability_zone', 'nova'), ('host', 'host1')]: - self.vol2.__setitem__(key, val) - - self.secgrp1 = Volume() - for key, val in [('id', 1), ('ec2_id', 'default')]: - self.secgrp1.__setitem__(key, val) - - self.secgrp2 = Volume() - for key, val in [('id', 2), ('ec2_id', 'def2')]: - self.secgrp2.__setitem__(key, val) - - self.netref1 = Network() - - def setMocks(self): - - # mocks for pre_live_migration - self.ctxt = context.get_admin_context() - db.instance_get = Mock(return_value=self.instance1) - db.volume_get_by_ec2_id = Mock(return_value=[self.vol1, self.vol2]) - db.volume_get_shelf_and_blade = Mock(return_value=(3, 4)) - db.instance_get_fixed_address = Mock(return_value=self.fixed_ip1) - db.security_group_get_by_instance \ - = Mock(return_value=[self.secgrp1, self.secgrp2]) - self.manager.driver.setup_nwfilters_for_instance \ - = Mock(return_value=None) - self.manager.driver.nwfilter_for_instance_exists = Mock(return_value=None) - self.manager.network_manager.setup_compute_network \ - = Mock(return_value=None) - # mocks for live_migration_ - rpc.call = Mock(return_value=True) - db.instance_set_state = Mock(return_value=True) - - # ---> test for nova.compute.manager.pre_live_migration() - def test01(self): - """01: NotFound error occurs on finding instance on DB. """ - - db.instance_get = Mock(side_effect=exception.NotFound('ERR')) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test02(self): - """02: NotAuthrized occurs on finding volume on DB. """ - - db.volume_get_by_ec2_id \ - = Mock(side_effect=exception.NotAuthorized('ERR')) - - self.assertRaises(exception.NotAuthorized, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test03(self): - """03: Unexpected exception occurs on finding volume on DB. """ - - db.volume_get_by_ec2_id = Mock(side_effect=TypeError('ERR')) - - self.assertRaises(TypeError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test04(self): - """04: no volume and fixed ip found on DB, """ - - db.volume_get_by_ec2_id = Mock(side_effect=exception.NotFound('ERR')) - db.instance_get_fixed_address = Mock(return_value=None) - - self.assertRaises(rpc.RemoteError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - c1 = (0 <= sys.stderr.buffer.find('has no volume')) - - self.assertEqual(c1, True) - - def test05(self): - """05: volume found and no fixed_ip found on DB. """ - - db.instance_get_fixed_address \ - = Mock(side_effect=exception.NotFound('ERR')) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test06(self): - """06: self.driver.setup_nwfilters_for_instance causes NotFound. """ - self.manager.driver.setup_nwfilters_for_instance \ - = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test07(self): - """07: self.network_manager.setup_compute_network causes ProcessExecutionError. """ - self.manager.network_manager.setup_compute_network \ - = Mock(side_effect=exception.ProcessExecutionError("ERR")) - - self.assertRaises(exception.ProcessExecutionError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - - def test08(self): - """08: self.manager.network_manager.setup_compute_network - exception.NotFound. """ - self.manager.network_manager.setup_compute_network \ - = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - # those 2 cases are omitted : - # self.driver.setup_nwfilters_for_instance causes - # twisted.python.failure.Failure. - # self.driver.refresh_security_group causes twisted.python.failure.Failure. - # - # twisted.python.failure.Failure can not be used with assertRaises, - # it doesnt have __call___ - # - - def test09(self): - """09: volume/fixed_ip found on DB, all procedure finish - successfully.. """ - - result = self.manager.pre_live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - self.assertEqual(result, True) - - # ---> test for nova.compute.manager.live_migration() - - def test10(self): - """10: rpc.call(pre_live_migration returns Error(Not None). """ - rpc.call = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test11(self): - """11: if rpc.call returns rpc.RemoteError. """ - - rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) - db.instance_set_state = Mock(return_value=True) - result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - c1 = (None == result) - c2 = (0 <= sys.stderr.buffer.find('err at')) - self.assertEqual(c1 and c2, True) - - def test12(self): - """12: if rpc.call returns rpc.RemoteError and instance_set_state - also ends up err. (then , unexpected err occurs, in this case - TypeError) - """ - rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) - db.instance_set_state = Mock(side_effect=TypeError("ERR")) - self.assertRaises(TypeError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test13(self): - """13: if wait for pre_live_migration, but timeout. """ - rpc.call = dummyCall - - db.instance_get = Mock(return_value=self.instance1) - - result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - c1 = (None == result) - c2 = (0 <= sys.stderr.buffer.find('Timeout for')) - self.assertEqual(c1 and c2, True) - - def test14(self): - """14: if db_instance_get issues NotFound. - """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(side_effect=exception.NotFound("ERR")) - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test15(self): - """15: if rpc.call returns True, and instance_get() cause other - exception. (Unexpected case - b/c it already checked by - nova-manage) - """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(side_effect=TypeError("ERR")) - - self.assertRaises(TypeError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test16(self): - """16: if rpc.call returns True, and live_migration issues - ProcessExecutionError. """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(return_value=self.instance1) - ret = self.manager.driver.live_migration \ - = Mock(side_effect=utils.ProcessExecutionError("ERR")) - - self.assertRaises(utils.ProcessExecutionError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test17(self): - """17: everything goes well. """ - self.manager.driver.live_migration = Mock(return_value=True) - ret = self.manager.live_migration(self.ctxt, 'i-12345', 'host1') - self.assertEqual(True, True) - - def tearDown(self): - """common terminating method. """ - self.stderr.realFlush() - sys.stderr = self.stderrBak - #sys.stdout = self.stdoutBak - -if __name__ == '__main__': - logging.getLogger().setLevel(logging.DEBUG) - #unittest.main() - - suite = unittest.TestLoader().loadTestsFromTestCase(ComputeTestFunctions) - unittest.TextTestRunner(verbosity=2).run(suite) - - #suite = unittest.TestSuite() - #suite.addTest(ComputeTestFunctions("test15")) - #suite.addTest(ComputeTestFunctions("test16")) - #unittest.TextTestRunner(verbosity=2).run(suite) ->>>>>>> MERGE-SOURCE diff --git a/nova/livemigration_test/UT/libvirtConnection.test.py b/nova/livemigration_test/UT/libvirtConnection.test.py deleted file mode 100644 index 093d19ea3..000000000 --- a/nova/livemigration_test/UT/libvirtConnection.test.py +++ /dev/null @@ -1,741 +0,0 @@ -<<<<<<< TREE -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -NOVA_DIR='/opt/nova-2010.4' - -import sys -import unittest -import commands -import re -import logging -import libvirt - -from mock import Mock -import twisted - -try : - print - print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.compute.manager import ComputeManager - from nova.virt import libvirt_conn - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova import process - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set PYTHONPATH to nova-install-dir' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def writelines(self, arg): - self.buffer += arg - def flush(self): - print 'flush' - self.buffer = '' - -class tmpStderr(tmpStdout): - def write(self,arg): - self.buffer += arg - def flush(self): - pass - def realFlush(self): - self.buffer = '' - -class DummyLibvirtConn(object): - nwfilterLookupByName = None - def __init__(self): - pass - - -class LibvirtConnectionTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - manager = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - #if self.stdout is None: - # self.__class__.stdout = tmpStdout() - #self.stdoutBak = sys.stdout - #sys.stdout = self.stdout - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - self.host = 'openstack2-api' - if self.manager is None: - self.__class__.manager = libvirt_conn.get_connection(False) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - for key, val in [ ('name', 'host1'), ('cpu', 5), ('memory_mb', 20480), ('hdd_gb', 876) ]: - self.host1.__setitem__(key, val) - - self.instance1 = Instance() - for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5), ('internal_id',12345) ]: - self.instance1.__setitem__(key, val) - - - self.instance2 = Instance() - for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance2.__setitem__(key, val) - - - self.fixed_ip1 = FixedIp() - for key, val in [ ('id', 1), ('address', '1.1.1.1'), ('network_id', '1'), - ('instance_id', 1)]: - self.fixed_ip1.__setitem__(key, val) - - self.floating_ip1 = FloatingIp() - for key, val in [ ('id', 1), ('address', '1.1.1.200') ]: - self.floating_ip1.__setitem__(key, val) - - self.netref1 = Network() - for key, val in [ ('id', 1) ]: - self.netref1.__setitem__(key, val) - - - def setMocks(self): - - self.ctxt = context.get_admin_context() - db.instance_get_fixed_address = Mock(return_value = '1.1.1.1') - db.fixed_ip_update = Mock(return_value = None) - db.fixed_ip_get_network = Mock(return_value = self.netref1) - db.network_update = Mock(return_value = None) - db.instance_get_floating_address = Mock(return_value = '1.1.1.200') - db.floating_ip_get_by_address = Mock(return_value = self.floating_ip1) - db.floating_ip_update = Mock(return_value = None) - db.instance_update = Mock(return_value = None) - - - # ---> test for nova.virt.libvirt_conn.nwfilter_for_instance_exists() - - def test01(self): - """01: libvirt.libvirtError occurs. """ - - self.manager._wrapped_conn = DummyLibvirtConn() - self.manager._test_connection = Mock(return_value=True) - self.manager._conn.nwfilterLookupByName = \ - Mock(side_effect=libvirt.libvirtError("ERR")) - ret = self.manager.nwfilter_for_instance_exists(self.instance1) - self.assertEqual(ret, False) - - def test02(self): - """02: libvirt.libvirtError not occurs. """ - - self.manager._wrapped_conn = DummyLibvirtConn() - self.manager._test_connection = Mock(return_value=True) - self.manager._conn.nwfilterLookupByName = \ - Mock(return_value=True) - ret = self.manager.nwfilter_for_instance_exists(self.instance1) - self.assertEqual(ret, True) - - # ---> test for nova.virt.libvirt_conn.live_migraiton() - - def test03(self): - """03: Unexpected exception occurs on finding volume on DB. """ - - utils.execute = Mock( side_effect=process.ProcessExecutionError('ERR') ) - - self.assertRaises(process.ProcessExecutionError, - self.manager.live_migration, - self.instance1, - 'host2') - - # ---> other case cannot be tested because live_migraiton - # is synchronized/asynchronized method are mixed together - - - # ---> test for nova.virt.libvirt_conn._post_live_migraiton - - def test04(self): - """04: instance_ref is not nova.db.sqlalchemy.models.Instances""" - - self.assertRaises(TypeError, - self.manager._post_live_migration, - "dummy string", - 'host2') - - def test05(self): - """05: db.instance_get_fixed_address return None""" - - db.instance_get_fixed_address = Mock( return_value=None ) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('fixed_ip is not found')) - self.assertEqual(c1 and c2, True) - - def test06(self): - """06: db.instance_get_fixed_address raises NotFound""" - - db.instance_get_fixed_address = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.instance1, - 'host2') - - def test07(self): - """07: db.instance_get_fixed_address raises Unknown exception""" - - db.instance_get_fixed_address = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test08(self): - """08: db.fixed_ip_update return NotFound. """ - - db.fixed_ip_update = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test09(self): - """09: db.fixed_ip_update return NotAuthorized. """ - db.fixed_ip_update = Mock( side_effect=exception.NotAuthorized('ERR') ) - self.assertRaises(exception.NotAuthorized, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test10(self): - """10: db.fixed_ip_update return Unknown exception. """ - db.fixed_ip_update = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test11(self): - """11: db.fixed_ip_get_network causes NotFound. """ - - db.fixed_ip_get_network = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.instance1, - 'host1') - - # not tested db.fixed_ip_get_network raises NotAuthorized - # because same test has been done at previous test. - - def test12(self): - """12: db.fixed_ip_get_network causes Unknown exception. """ - - db.fixed_ip_get_network = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test13(self): - """13: db.network_update raises Unknown exception. """ - db.network_update = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test14(self): - """14: db.instance_get_floating_address raises NotFound. """ - db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - - def test15(self): - """15: db.instance_get_floating_address returns None. """ - - db.instance_get_floating_address = Mock( return_value=None ) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('floating_ip is not found')) - self.assertEqual(c1 and c2, True) - - def test16(self): - """16: db.instance_get_floating_address raises NotFound. """ - - db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - def test17(self): - """17: db.instance_get_floating_address raises Unknown exception. """ - db.instance_get_floating_address = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - - def test18(self): - """18: db.floating_ip_get_by_address raises NotFound """ - - db.floating_ip_get_by_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - def test19(self): - """19: db.floating_ip_get_by_address raises Unknown exception. """ - db.floating_ip_get_by_address = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - - def test20(self): - """20: db.floating_ip_update raises Unknown exception. - """ - db.floating_ip_update = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - def test21(self): - """21: db.instance_update raises unknown exception. """ - - db.instance_update = Mock(side_effect=TypeError("ERR")) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def tearDown(self): - """common terminating method. """ - self.stderr.realFlush() - sys.stderr = self.stderrBak - #sys.stdout = self.stdoutBak - -if __name__ == '__main__': - logging.getLogger().setLevel(logging.DEBUG) - #unittest.main() - - suite = unittest.TestLoader().loadTestsFromTestCase(LibvirtConnectionTestFunctions) - unittest.TextTestRunner(verbosity=2).run(suite) - - #suite = unittest.TestSuite() - #suite.addTest(LibvirtConnectionTestFunctions("test14")) - #suite.addTest(LibvirtConnectionTestFunctions("test16")) - #unittest.TextTestRunner(verbosity=2).run(suite) - - -======= -#!/usr/bin/python -# -*- coding: UTF-8 -*- - - -import sys -import os -import unittest -import commands -import re -import logging -import libvirt - -from mock import Mock -import twisted - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - - -try : - print - print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.compute.manager import ComputeManager - from nova.virt import libvirt_conn - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova import process - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def writelines(self, arg): - self.buffer += arg - def flush(self): - print 'flush' - self.buffer = '' - -class tmpStderr(tmpStdout): - def write(self,arg): - self.buffer += arg - def flush(self): - pass - def realFlush(self): - self.buffer = '' - -class DummyLibvirtConn(object): - nwfilterLookupByName = None - def __init__(self): - pass - - -class LibvirtConnectionTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - manager = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - #if self.stdout is None: - # self.__class__.stdout = tmpStdout() - #self.stdoutBak = sys.stdout - #sys.stdout = self.stdout - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - self.host = 'openstack2-api' - if self.manager is None: - self.__class__.manager = libvirt_conn.get_connection(False) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - for key, val in [ ('name', 'host1'), ('cpu', 5), ('memory_mb', 20480), ('hdd_gb', 876) ]: - self.host1.__setitem__(key, val) - - self.instance1 = Instance() - for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5), ('internal_id',12345) ]: - self.instance1.__setitem__(key, val) - - - self.instance2 = Instance() - for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance2.__setitem__(key, val) - - - self.fixed_ip1 = FixedIp() - for key, val in [ ('id', 1), ('address', '1.1.1.1'), ('network_id', '1'), - ('instance_id', 1)]: - self.fixed_ip1.__setitem__(key, val) - - self.floating_ip1 = FloatingIp() - for key, val in [ ('id', 1), ('address', '1.1.1.200') ]: - self.floating_ip1.__setitem__(key, val) - - self.netref1 = Network() - for key, val in [ ('id', 1) ]: - self.netref1.__setitem__(key, val) - - - def setMocks(self): - - self.ctxt = context.get_admin_context() - db.instance_get_fixed_address = Mock(return_value = '1.1.1.1') - db.fixed_ip_update = Mock(return_value = None) - db.fixed_ip_get_network = Mock(return_value = self.netref1) - db.network_update = Mock(return_value = None) - db.instance_get_floating_address = Mock(return_value = '1.1.1.200') - db.floating_ip_get_by_address = Mock(return_value = self.floating_ip1) - db.floating_ip_update = Mock(return_value = None) - db.instance_update = Mock(return_value = None) - - - # ---> test for nova.virt.libvirt_conn.nwfilter_for_instance_exists() - - def test01(self): - """01: libvirt.libvirtError occurs. """ - - self.manager._wrapped_conn = DummyLibvirtConn() - self.manager._test_connection = Mock(return_value=True) - self.manager._conn.nwfilterLookupByName = \ - Mock(side_effect=libvirt.libvirtError("ERR")) - ret = self.manager.nwfilter_for_instance_exists(self.instance1) - self.assertEqual(ret, False) - - def test02(self): - """02: libvirt.libvirtError not occurs. """ - - self.manager._wrapped_conn = DummyLibvirtConn() - self.manager._test_connection = Mock(return_value=True) - self.manager._conn.nwfilterLookupByName = \ - Mock(return_value=True) - ret = self.manager.nwfilter_for_instance_exists(self.instance1) - self.assertEqual(ret, True) - - # ---> test for nova.virt.libvirt_conn.live_migraiton() - - def test03(self): - """03: Unexpected exception occurs on finding volume on DB. """ - - utils.execute = Mock( side_effect=process.ProcessExecutionError('ERR') ) - - self.assertRaises(process.ProcessExecutionError, - self.manager.live_migration, - self.instance1, - 'host2') - - # ---> other case cannot be tested because live_migraiton - # is synchronized/asynchronized method are mixed together - - - # ---> test for nova.virt.libvirt_conn._post_live_migraiton - - def test04(self): - """04: instance_ref is not nova.db.sqlalchemy.models.Instances""" - - self.assertRaises(TypeError, - self.manager._post_live_migration, - "dummy string", - 'host2') - - def test05(self): - """05: db.instance_get_fixed_address return None""" - - db.instance_get_fixed_address = Mock( return_value=None ) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('fixed_ip is not found')) - self.assertEqual(c1 and c2, True) - - def test06(self): - """06: db.instance_get_fixed_address raises NotFound""" - - db.instance_get_fixed_address = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.instance1, - 'host2') - - def test07(self): - """07: db.instance_get_fixed_address raises Unknown exception""" - - db.instance_get_fixed_address = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test08(self): - """08: db.fixed_ip_update return NotFound. """ - - db.fixed_ip_update = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test09(self): - """09: db.fixed_ip_update return NotAuthorized. """ - db.fixed_ip_update = Mock( side_effect=exception.NotAuthorized('ERR') ) - self.assertRaises(exception.NotAuthorized, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test10(self): - """10: db.fixed_ip_update return Unknown exception. """ - db.fixed_ip_update = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test11(self): - """11: db.fixed_ip_get_network causes NotFound. """ - - db.fixed_ip_get_network = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.instance1, - 'host1') - - # not tested db.fixed_ip_get_network raises NotAuthorized - # because same test has been done at previous test. - - def test12(self): - """12: db.fixed_ip_get_network causes Unknown exception. """ - - db.fixed_ip_get_network = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test13(self): - """13: db.network_update raises Unknown exception. """ - db.network_update = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def test14(self): - """14: db.instance_get_floating_address raises NotFound. """ - db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - - def test15(self): - """15: db.instance_get_floating_address returns None. """ - - db.instance_get_floating_address = Mock( return_value=None ) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('floating_ip is not found')) - self.assertEqual(c1 and c2, True) - - def test16(self): - """16: db.instance_get_floating_address raises NotFound. """ - - db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - def test17(self): - """17: db.instance_get_floating_address raises Unknown exception. """ - db.instance_get_floating_address = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - - def test18(self): - """18: db.floating_ip_get_by_address raises NotFound """ - - db.floating_ip_get_by_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - def test19(self): - """19: db.floating_ip_get_by_address raises Unknown exception. """ - db.floating_ip_get_by_address = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - - def test20(self): - """20: db.floating_ip_update raises Unknown exception. - """ - db.floating_ip_update = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - def test21(self): - """21: db.instance_update raises unknown exception. """ - - db.instance_update = Mock(side_effect=TypeError("ERR")) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.instance1, - 'host1') - - def tearDown(self): - """common terminating method. """ - self.stderr.realFlush() - sys.stderr = self.stderrBak - #sys.stdout = self.stdoutBak - -if __name__ == '__main__': - logging.getLogger().setLevel(logging.DEBUG) - #unittest.main() - - suite = unittest.TestLoader().loadTestsFromTestCase(LibvirtConnectionTestFunctions) - unittest.TextTestRunner(verbosity=2).run(suite) - - #suite = unittest.TestSuite() - #suite.addTest(LibvirtConnectionTestFunctions("test14")) - #suite.addTest(LibvirtConnectionTestFunctions("test16")) - #unittest.TextTestRunner(verbosity=2).run(suite) - - ->>>>>>> MERGE-SOURCE diff --git a/nova/livemigration_test/UT/nova-manage.test.py b/nova/livemigration_test/UT/nova-manage.test.py deleted file mode 100644 index b39af3c60..000000000 --- a/nova/livemigration_test/UT/nova-manage.test.py +++ /dev/null @@ -1,672 +0,0 @@ -<<<<<<< TREE -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -NOVA_DIR='/opt/nova-2010.2' - -import sys -import unittest -import commands -import re - -from mock import Mock - -try : - print - print 'Testing %s/bin/nova-manage, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set PYTHONPATH to nova-install-dir' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def flush(self): - self.buffer = '' - - -class NovaManageTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - - hostCmds = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - commands.getstatusoutput('cp -f %s/bin/nova-manage %s' % ( NOVA_DIR, self.getNovaManageCopyPath() )) - commands.getstatusoutput('touch %s' % self.getInitpyPath() ) - try : - import bin.novamanagetest - except: - print 'Fail to import nova-manage . check bin/nova-manage exists' - raise - - # replace stdout for checking nova-manage output - if self.stdout is None : - self.__class__.stdout = tmpStdout() - self.stdoutBak = sys.stdout - sys.stdout = self.stdout - - # prepare test data - self.setTestData() - - - def setTestData(self): - import bin.novamanagetest - - if self.hostCmds is None : - self.__class__.hostCmds = bin.novamanagetest.HostCommands() - self.instanceCmds = bin.novamanagetest.InstanceCommands() - - self.host1 = Host() - self.host1.__setitem__('name', 'host1') - - self.host2 = Host() - self.host2.__setitem__('name', 'host2') - - self.instance1 = Instance() - self.instance1.__setitem__('id', 1) - self.instance1.__setitem__('host', 'host1') - self.instance1.__setitem__('hostname', 'i-12345') - self.instance1.__setitem__('state', power_state.NOSTATE) - self.instance1.__setitem__('state_description', 'running') - - self.instance2 = Instance() - self.instance2.__setitem__('id', 2) - self.instance2.__setitem__('host', 'host1') - self.instance2.__setitem__('hostname', 'i-12345') - self.instance2.__setitem__('state', power_state.RUNNING) - self.instance2.__setitem__('state_description', 'pending') - - self.instance3 = Instance() - self.instance3.__setitem__('id', 3) - self.instance3.__setitem__('host', 'host1') - self.instance3.__setitem__('hostname', 'i-12345') - self.instance3.__setitem__('state', power_state.RUNNING) - self.instance3.__setitem__('state_description', 'running') - - db.host_get_all = Mock(return_value=[self.host1, self.host2]) - - def getInitpyPath(self): - return '%s/bin/__init__.py' % NOVA_DIR - - def getNovaManageCopyPath(self): - return '%s/bin/novamanagetest.py' % NOVA_DIR - - # -----> Test for nova-manage host list - - def test01(self): - """01: Got some host lists. """ - - self.hostCmds.list() - - c1 = (2 == self.stdout.buffer.count('\n')) - c2 = (0 <= self.stdout.buffer.find('host1')) - c3 = (0 <= self.stdout.buffer.find('host2')) - self.assertEqual(c1 and c2 and c3, True) - - def test02(self): - """02: Got empty lsit. """ - - db.host_get_all = Mock(return_value=[]) - self.hostCmds.list() - - # result should be empty - c = (0 == len(self.stdout.buffer) ) - self.assertEqual(c, True) - - def test03(self): - """03: Got notFound """ - - db.host_get_all = Mock(side_effect=exception.NotFound("ERR")) - self.assertRaises(exception.NotFound, self.hostCmds.list) - - # --------> Test For nova-manage host show - - def test04(self): - """04: args are not enough(nova-manage host show) """ - self.assertRaises(TypeError, self.hostCmds.show ) - - - def test05(self): - """05: nova-manage host show not-registered-host, and got an error""" - - rpc.call = Mock(return_value={'ret' : False, 'msg': 'ERR'} ) - self.hostCmds.show('host1') - self.assertEqual( self.stdout.buffer[:3]=='ERR', True ) - - - def test06(self): - """06: nova-manage host show registerd-host, and no project uses the host""" - - dic = {'ret': True, - 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, - 'usage': {}} - - rpc.call = Mock(return_value=dic ) - self.hostCmds.show('host1') - - # result should be : - # HOST PROJECT cpu mem(mb) disk(gb) - # host1 1 2 3 - line = self.stdout.buffer.split('\n')[1] - line = re.compile('\t+').sub(' ', line).strip() - c1 = ( 'host1 1 2 3' == line ) - c2 = ( self.stdout.buffer.count('\n') == 2 ) - - self.assertEqual( c1 and c2, True ) - - def test07(self): - """07: nova-manage host show registerd-host, - and some projects use the host - """ - dic = {'ret': True, - 'phy_resource': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, - 'usage': {'p1': {'cpu':1, 'memory_mb':2, 'hdd_gb':3}, - 'p2': {'cpu':1, 'memory_mb':2, 'hdd_gb':3} }} - - rpc.call = Mock(return_value=dic ) - self.hostCmds.show('host1') - - # result should be : - # HOST PROJECT cpu mem(mb) disk(gb) - # host1 1 2 3 - # host1 p1 1 2 3 - # host1 p2 4 5 6 - line = self.stdout.buffer.split('\n')[1] - ret = re.compile('\t+').sub(' ', line).strip() - c1 = ( 'host1 1 2 3' == ret ) - - line = self.stdout.buffer.split('\n')[2] - line = re.compile('\t+').sub(' ', line).strip() - c2 = ( 'host1 p1 1 2 3' == line ) or ( 'host1 p2 1 2 3' == line ) - - line = self.stdout.buffer.split('\n')[3] - ret = re.compile('\t+').sub(' ', line).strip() - c3 = ( 'host1 p1 1 2 3' == ret ) or ( 'host1 p2 1 2 3' == ret ) - - self.assertEqual( c1 and c2 and c3, True ) - - def test08(self): - """08: nova-manage host show registerd-host, and rpc.call returns None - (unexpected error) - """ - rpc.call = Mock(return_value=None ) - self.hostCmds.show('host1') - c1 = ( 0 <= self.stdout.buffer.find('Unexpected error') ) - self.assertEqual( c1, True ) - - # ----------> Test for bin/nova-manage instance live_migration - - def test09(self): - """09: arguments are not enough(nova-manage instances live_migration) - """ - self.assertRaises(TypeError, self.instanceCmds.live_migration ) - - def test10(self): - """10: arguments are not enough(nova-manage instances live_migration ec2_id) - """ - self.assertRaises(TypeError, self.instanceCmds.live_migration, 'i-xxx' ) - - def test11(self): - """11: nova-manage instances live_migration ec2_id host, - where hostname is invalid - """ - db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) - - def test12(self): - """12: nova-manage instances live_migration ec2_id(invalid id) host""" - - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( side_effect=exception.NotFound('ERR') ) - - self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) - - def test13(self): - """13: nova-manage instances live_migration ec2_id host, - but instance specifed by ec2 id is not running (state is not power_state.RUNNING) - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) - c1 = c2 = False - try : - self.instanceCmds.live_migration('i-12345', 'host1') - except SystemExit, e: - c1 = (1 == e.code) - c2 = (0 < self.stdout.buffer.find('is not running') ) - self.assertEqual( c1 and c2 , True ) - - - def test14(self): - """14: nova-manage instances live_migration ec2_id host, - but instance specifed by ec2 id is not running (state_description is not running) - """ - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) - c1 = c2 = False - try : - self.instanceCmds.live_migration('i-12345', 'host2') - except SystemExit, e: - c1 = (1 == e.code) - c2 = (0 < self.stdout.buffer.find('is not running') ) - self.assertEqual( c1 and c2 , True ) - - def test15(self): - """15: nova-manage instances live_migration ec2_id host, - but instance is running at the same host specifed above, so err should be occured. - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - c1 = c2 = False - try : - self.instanceCmds.live_migration('i-12345', 'host1') - except SystemExit, e: - c1 = (2 == e.code) - c2 = (0 < self.stdout.buffer.find('is running now') ) - self.assertEqual( c1 and c2 , True ) - - def test16(self): - """16: nova-manage instances live_migration ec2_id host, - everything goes well, ang gets success messages. - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - rpc.cast = Mock(return_value = None) - - self.instanceCmds.live_migration('i-12345', 'host2') - c1 = (0 <= self.stdout.buffer.find('Finished all procedure') ) - self.assertEqual( c1, True ) - - - def tearDown(self): - """common terminating method. """ - commands.getstatusoutput('rm -rf %s' % self.getInitpyPath() ) - commands.getstatusoutput('rm -rf %s' % self.getNovaManageCopyPath() ) - sys.stdout.flush() - sys.stdout = self.stdoutBak - -if __name__ == '__main__': - #unittest.main() - suite = unittest.TestLoader().loadTestsFromTestCase(NovaManageTestFunctions) - unittest.TextTestRunner(verbosity=3).run(suite) - - -======= -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -NOVA_DIR='/opt/nova-2010.4' - -import sys -import os -import unittest -import commands -import re - -from mock import Mock - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - - -try : - print - print 'Testing %s/bin/nova-manage, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def flush(self): - self.buffer = '' - -class tmpStderr(tmpStdout): - def write(self, arg): - self.buffer += arg - def flush(self): - pass - def realFlush(self): - self.buffer = '' - - -class NovaManageTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - - hostCmds = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - commands.getstatusoutput('cp -f %s/bin/nova-manage %s' % ( NOVA_DIR, self.getNovaManageCopyPath() )) - commands.getstatusoutput('touch %s' % self.getInitpyPath() ) - try : - import bin.novamanagetest - except: - print 'Fail to import nova-manage . check bin/nova-manage exists' - raise - - # replace stdout for checking nova-manage output - if self.stdout is None : - self.__class__.stdout = tmpStdout() - self.stdoutBak = sys.stdout - sys.stdout = self.stdout - - # replace stderr for checking nova-manage output - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - # prepare test data - self.setTestData() - - - def setTestData(self): - import bin.novamanagetest - - if self.hostCmds is None : - self.__class__.hostCmds = bin.novamanagetest.HostCommands() - self.instanceCmds = bin.novamanagetest.InstanceCommands() - - self.host1 = Host() - self.host1.__setitem__('name', 'host1') - - self.host2 = Host() - self.host2.__setitem__('name', 'host2') - - self.instance1 = Instance() - self.instance1.__setitem__('id', 1) - self.instance1.__setitem__('host', 'host1') - self.instance1.__setitem__('hostname', 'i-12345') - self.instance1.__setitem__('state', power_state.NOSTATE) - self.instance1.__setitem__('state_description', 'running') - - self.instance2 = Instance() - self.instance2.__setitem__('id', 2) - self.instance2.__setitem__('host', 'host1') - self.instance2.__setitem__('hostname', 'i-12345') - self.instance2.__setitem__('state', power_state.RUNNING) - self.instance2.__setitem__('state_description', 'pending') - - self.instance3 = Instance() - self.instance3.__setitem__('id', 3) - self.instance3.__setitem__('host', 'host1') - self.instance3.__setitem__('hostname', 'i-12345') - self.instance3.__setitem__('state', power_state.RUNNING) - self.instance3.__setitem__('state_description', 'running') - - db.host_get_all = Mock(return_value=[self.host1, self.host2]) - - def getInitpyPath(self): - return '%s/bin/__init__.py' % NOVA_DIR - - def getNovaManageCopyPath(self): - return '%s/bin/novamanagetest.py' % NOVA_DIR - - # -----> Test for nova-manage host list - - def test01(self): - """01: Got some host lists. """ - - self.hostCmds.list() - - c1 = (2 == self.stdout.buffer.count('\n')) - c2 = (0 <= self.stdout.buffer.find('host1')) - c3 = (0 <= self.stdout.buffer.find('host2')) - self.assertEqual(c1 and c2 and c3, True) - - def test02(self): - """02: Got empty lsit. """ - - db.host_get_all = Mock(return_value=[]) - self.hostCmds.list() - - # result should be empty - c = (0 == len(self.stdout.buffer) ) - self.assertEqual(c, True) - - def test03(self): - """03: Got notFound """ - - db.host_get_all = Mock(side_effect=exception.NotFound("ERR")) - self.assertRaises(exception.NotFound, self.hostCmds.list) - - # --------> Test For nova-manage host show - - def test04(self): - """04: args are not enough(nova-manage host show) """ - self.assertRaises(TypeError, self.hostCmds.show ) - - - def test05(self): - """05: nova-manage host show not-registered-host, and got an error""" - - rpc.call = Mock(return_value={'ret' : False, 'msg': 'ERR'} ) - self.hostCmds.show('host1') - self.assertEqual( self.stdout.buffer[:3]=='ERR', True ) - - - def test06(self): - """06: nova-manage host show registerd-host, and no project uses the host""" - - dic = {'ret': True, - 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, - 'usage': {}} - - rpc.call = Mock(return_value=dic ) - self.hostCmds.show('host1') - - # result should be : - # HOST PROJECT cpu mem(mb) disk(gb) - # host1 1 2 3 - line = self.stdout.buffer.split('\n')[1] - line = re.compile('\t+').sub(' ', line).strip() - c1 = ( 'host1 1 2 3' == line ) - c2 = ( self.stdout.buffer.count('\n') == 2 ) - - self.assertEqual( c1 and c2, True ) - - def test07(self): - """07: nova-manage host show registerd-host, - and some projects use the host - """ - dic = {'ret': True, - 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, - 'usage': {'p1': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, - 'p2': {'vcpus':1, 'memory_mb':2, 'local_gb':3} }} - - rpc.call = Mock(return_value=dic ) - self.hostCmds.show('host1') - - # result should be : - # HOST PROJECT cpu mem(mb) disk(gb) - # host1 1 2 3 - # host1 p1 1 2 3 - # host1 p2 4 5 6 - line = self.stdout.buffer.split('\n')[1] - ret = re.compile('\t+').sub(' ', line).strip() - c1 = ( 'host1 1 2 3' == ret ) - - line = self.stdout.buffer.split('\n')[2] - line = re.compile('\t+').sub(' ', line).strip() - c2 = ( 'host1 p1 1 2 3' == line ) or ( 'host1 p2 1 2 3' == line ) - - line = self.stdout.buffer.split('\n')[3] - ret = re.compile('\t+').sub(' ', line).strip() - c3 = ( 'host1 p1 1 2 3' == ret ) or ( 'host1 p2 1 2 3' == ret ) - - self.assertEqual( c1 and c2 and c3, True ) - - def test08(self): - """08: nova-manage host show registerd-host, and rpc.call returns None - (unexpected error) - """ - rpc.call = Mock(return_value=None ) - self.hostCmds.show('host1') - c1 = ( 0 <= self.stdout.buffer.find('Unexpected error') ) - self.assertEqual( c1, True ) - - # ----------> Test for bin/nova-manage instance live_migration - - def test09(self): - """09: arguments are not enough(nova-manage instances live_migration) - """ - self.assertRaises(TypeError, self.instanceCmds.live_migration ) - - def test10(self): - """10: arguments are not enough(nova-manage instances live_migration ec2_id) - """ - self.assertRaises(TypeError, self.instanceCmds.live_migration, 'i-xxx' ) - - def test11(self): - """11: nova-manage instances live_migration ec2_id host, - where hostname is invalid - """ - db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) - - def test12(self): - """12: nova-manage instances live_migration ec2_id(invalid id) host""" - - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( side_effect=exception.NotFound('ERR') ) - - self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) - - def test13(self): - """13: nova-manage instances live_migration ec2_id host, - but instance specifed by ec2 id is not running (state is not power_state.RUNNING) - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) - try : - self.instanceCmds.live_migration('i-12345', 'host1') - except exception.Invalid, e: - c1 = (0 < e.message.find('is not running') ) - self.assertTrue(c1, True) - return False - - - def test14(self): - """14: nova-manage instances live_migration ec2_id host, - but instance specifed by ec2 id is not running (state_description is not running) - """ - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) - try : - self.instanceCmds.live_migration('i-12345', 'host2') - except exception.Invalid, e: - c1 = (0 < e.message.find('is not running') ) - self.assertTrue(c1, True) - return False - - def test15(self): - """15: nova-manage instances live_migration ec2_id host, - but instance is running at the same host specifed above, so err should be occured. - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - try : - self.instanceCmds.live_migration('i-12345', 'host1') - except exception.Invalid, e: - c1 = ( 0 <= e.message.find('is running now') ) - self.assertTrue(c1, True) - return False - - - def test16(self): - """16: nova-manage instances live_migration ec2_id host, - rpc.call raises RemoteError because destination doesnt have enough resource. - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - rpc.call = Mock(return_value = rpc.RemoteError(TypeError, 'val', 'traceback')) - self.assertRaises(rpc.RemoteError, self.instanceCmds.live_migration, 'i-xxx', 'host2' ) - - - def test17(self): - """17: nova-manage instances live_migration ec2_id host, - everything goes well, ang gets success messages. - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - rpc.call = Mock(return_value = None) - - self.instanceCmds.live_migration('i-12345', 'host2') - c1 = (0 <= self.stdout.buffer.find('Finished all procedure') ) - self.assertEqual( c1, True ) - - - def tearDown(self): - """common terminating method. """ - commands.getstatusoutput('rm -rf %s' % self.getInitpyPath() ) - commands.getstatusoutput('rm -rf %s' % self.getNovaManageCopyPath() ) - sys.stdout.flush() - sys.stdout = self.stdoutBak - self.stderr.realFlush() - sys.stderr = self.stderrBak - -if __name__ == '__main__': - #unittest.main() - suite = unittest.TestLoader().loadTestsFromTestCase(NovaManageTestFunctions) - unittest.TextTestRunner(verbosity=3).run(suite) - - ->>>>>>> MERGE-SOURCE diff --git a/nova/livemigration_test/UT/schedulerManager.test.py b/nova/livemigration_test/UT/schedulerManager.test.py deleted file mode 100644 index 2fd8bd511..000000000 --- a/nova/livemigration_test/UT/schedulerManager.test.py +++ /dev/null @@ -1,771 +0,0 @@ -<<<<<<< TREE -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -NOVA_DIR='/opt/nova-2010.2' - -import sys -import unittest -import commands -import re - -from mock import Mock - -try : - print - print 'Checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.scheduler.manager import SchedulerManager - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - -except: - print 'set PYTHONPATH to nova-install-dir' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def flush(self): - self.buffer = '' - - -class SchedulerTestFunctions(unittest.TestCase): - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - self.host = 'openstack2-api' - self.manager = SchedulerManager(host=self.host) - - self.setTestData() - - def setTestData(self): - - self.host1 = Host() - self.host1.__setitem__('name', 'host1') - self.host1.__setitem__('cpu', 5) - self.host1.__setitem__('memory_mb', 20480) - self.host1.__setitem__('hdd_gb', 876) - - self.host2 = Host() - self.host2.__setitem__('name', 'host2') - self.host2.__setitem__('cpu', 5) - self.host2.__setitem__('memory_mb', 20480) - self.host2.__setitem__('hdd_gb', 876) - - self.instance1 = Instance() - for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance1.__setitem__(key, val) - - - self.instance2 = Instance() - for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance2.__setitem__(key, val) - - - self.instance3 = Instance() - for key, val in [ ('id', 3), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance3.__setitem__(key, val) - - self.instance4 = Instance() - for key, val in [ ('id', 4), ('host', 'host2'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance4.__setitem__(key, val) - - self.instance5 = Instance() - for key, val in [ ('id', 5), ('host', 'host2'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance5.__setitem__(key, val) - - self.instance6 = Instance() - for key, val in [ ('id', 6), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance6.__setitem__(key, val) - - self.instance7 = Instance() - for key, val in [ ('id', 7), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 18432), ('local_gb', 5) ]: - self.instance7.__setitem__(key, val) - - self.instance8 = Instance() - for key, val in [ ('id', 8), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 866) ]: - self.instance8.__setitem__(key, val) - - - - def check_format(self, val): - """check result format of show_host_resource """ - - if dict != type(val) : - sys.stderr.write('return value is not dict') - return False - - if not val.has_key('ret'): - sys.stderr.write('invalid format(missing "ret"). ') - return False - - if not val['ret'] : - if not val.has_key('msg') : - sys.stderr.write( 'invalid format(missing "msg").' ) - return False - - else : - if not val.has_key('phy_resource') : - sys.stderr.write('invalid format(missing "phy_resource"). ') - return False - - if not val.has_key('usage'): - sys.stderr.write('invalid format(missing "usage"). ') - return False - - if not self._check_format(val['phy_resource']): - return False - - for key, dic in val['usage'].items() : - if not self._check_format(dic): - return False - return True - - def _check_format(self, val): - if dict != type(val) : - sys.stderr.write('return value is not dict') - return False - - for key in ['cpu', 'memory_mb', 'hdd_gb']: - if not val.has_key(key) : - sys.stderr.write('invalid format(missing "%s"). ' % key ) - return False - - return True - - # ---> test for nova.scheduler.manager.show_host_resource() - - def test01(self): - """01: get NotFound exception when dest host not found on DB """ - - ctxt = context.get_admin_context() - db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) - result = self.manager.show_host_resource(ctxt, 'not-registered-host') - c1 = ( not result['ret'] ) - c2 = ( 0 == result['msg'].find('No such') ) - self.assertEqual(c1 and c2, True) - - def test02(self): - """02: get other exception if unexpected err. """ - - ctxt = context.get_admin_context() - db.host_get_by_name = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, self.manager.show_host_resource, ctxt, 'host1' ) - - def test03(self): - """03: no instance found on dest host. """ - - ctxt = context.get_admin_context() - db.host_get_by_name = Mock( return_value = self.host1 ) - db.instance_get_all_by_host = Mock( return_value=[]) - ret= self.manager.show_host_resource(ctxt, 'host1') - - c1 = self.check_format(ret) - v = ret['phy_resource'] - c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) - c3 = ( 0 == len(ret['usage']) ) - - self.assertEqual(c1 and c2 and c3, True) - - def test04(self): - """04: some instance found on dest host. """ - - ctxt = context.get_admin_context() - db.host_get_by_name = Mock( return_value = self.host1 ) - db.instance_get_all_by_host = Mock( return_value=[ self.instance1, - self.instance2, - self.instance3] ) - - db.instance_get_vcpu_sum_by_host_and_project = Mock(return_value=3) - db.instance_get_memory_sum_by_host_and_project = Mock(return_value=1024) - db.instance_get_disk_sum_by_host_and_project = Mock(return_value=5) - - ret= self.manager.show_host_resource(ctxt, 'host1') - - c1 = self.check_format(ret) - v = ret['phy_resource'] - c2 = ( (5 == v['cpu']) and (20480 == v['memory_mb']) and (876 == v['hdd_gb'])) - c3 = ( 2 == len(ret['usage']) ) - c4 = ( self.instance1['project_id'] in ret['usage'].keys()) - c5 = ( self.instance3['project_id'] in ret['usage'].keys()) - - self.assertEqual(c1 and c2 and c3 and c4 and c5, True) - - - # ---> test for nova.scheduler.manager.has_enough_resource() - def test05(self): - """05: when cpu is exccded some instance found on dest host. """ - - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance6) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) - - def test06(self): - """06: when memory is exccded some instance found on dest host. """ - - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance7) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) - - def test07(self): - """07: when hdd is exccded some instance found on dest host. """ - - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance8) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) - - def test08(self): - """08: everything goes well. (instance_get_all_by_host returns list)""" - - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, True) - - - def test09(self): - """09: everything goes well(instance_get_all_by_host returns[]). """ - - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [] ) - - ret= self.manager.has_enough_resource(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, True) - - - # ---> test for nova.scheduler.manager.live_migration() - - - def test10(self): - """10: instance_get_by_internal_id issue NotFound. """ - # Mocks for has_enough_resource() - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance8) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - # Mocks for live_migration()db.instance_get_by_internal_id - # (any Mock is ok here. important mock is all above) - db.instance_get_by_internal_id = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.live_migration, - ctxt, - 'i-12345', - 'host1') - - - def test11(self): - """11: return False if host doesnt have enough resource. """ - - # Mocks for has_enough_resource() - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance8) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - # Mocks for live_migration()db.instance_get_by_internal_id - # (any Mock is ok here. important mock is all above) - db.instance_get_by_internal_id = Mock(return_value = self.instance8) - db.instance_set_state = Mock(return_value = True) - rpc_cast = Mock(return_value = True) - - ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, False) - - - - def test12(self): - """12: everything goes well. """ - - # Mocks for has_enough_resource() - ctxt = context.get_admin_context() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - # Mocks for live_migration()db.instance_get_by_internal_id - # (any Mock is ok here. important mock is all above) - db.instance_get_by_internal_id = Mock(return_value = self.instance8) - db.instance_set_state = Mock(return_value = True) - rpc.cast = Mock(return_value = True) - - ret= self.manager.live_migration(ctxt, 'i-12345', 'host1') - self.assertEqual(ret, True) - - - def tearDown(self): - """common terminating method. """ - #sys.stdout = self.stdoutBak - pass - -if __name__ == '__main__': - #unittest.main() - suite = unittest.TestLoader().loadTestsFromTestCase(SchedulerTestFunctions) - unittest.TextTestRunner(verbosity=3).run(suite) - - -======= -#!/usr/bin/python -# -*- coding: UTF-8 -*- - - -import sys -import os -import unittest -import commands -import re -import libvirt - -from mock import Mock - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - -try : - print - print 'Checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.scheduler.manager import SchedulerManager - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def flush(self): - self.buffer = '' - - -class SchedulerTestFunctions(unittest.TestCase): - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - self.host = 'openstack2-api' - self.manager = SchedulerManager(host=self.host) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - self.host1.__setitem__('name', 'host1') - self.host1.__setitem__('vcpus', 5) - self.host1.__setitem__('memory_mb', 20480) - self.host1.__setitem__('local_gb', 876) - - self.host2 = Host() - self.host2.__setitem__('name', 'host2') - self.host2.__setitem__('vcpus', 5) - self.host2.__setitem__('memory_mb', 20480) - self.host2.__setitem__('local_gb', 876) - self.host2.__setitem__('hypervisor_type', 'QEMU') - self.host2.__setitem__('hypervisor_version', 12003) - xml="x86_64NehalemIntel" - self.host2.__setitem__('cpu_info', xml) - - self.instance1 = Instance() - for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance1.__setitem__(key, val) - - - self.instance2 = Instance() - for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance2.__setitem__(key, val) - - - self.instance3 = Instance() - for key, val in [ ('id', 3), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance3.__setitem__(key, val) - - self.instance4 = Instance() - for key, val in [ ('id', 4), ('host', 'host2'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance4.__setitem__(key, val) - - self.instance5 = Instance() - for key, val in [ ('id', 5), ('host', 'host2'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance5.__setitem__(key, val) - - self.instance6 = Instance() - for key, val in [ ('id', 6), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance6.__setitem__(key, val) - - self.instance7 = Instance() - for key, val in [ ('id', 7), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 18432), ('local_gb', 5) ]: - self.instance7.__setitem__(key, val) - - self.instance8 = Instance() - for key, val in [ ('id', 8), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 866) ]: - self.instance8.__setitem__(key, val) - - self.service1 = Service() - for key, val in [ ('id', 1), ('host', 'host1'), ('binary', 'nova-compute'), - ('topic', 'compute')]: - self.service1.__setitem__(key, val) - - - def setMocks(self): - self.ctxt = context.get_admin_context() - # Mocks for has_enough_resource() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - # Mocks for live_migration - db.instance_get_by_internal_id = Mock(return_value = self.instance1) - # db.host_get_by_name <- defined above. - db.service_get_all_by_topic = Mock(return_value = [self.service1] ) - rpc.call = Mock(return_value=1) - - def check_format(self, val): - """check result format of show_host_resource """ - - if dict != type(val) : - sys.stderr.write('return value is not dict') - return False - - if not val.has_key('ret'): - sys.stderr.write('invalid format(missing "ret"). ') - return False - - if not val['ret'] : - if not val.has_key('msg') : - sys.stderr.write( 'invalid format(missing "msg").' ) - return False - - else : - if not val.has_key('phy_resource') : - sys.stderr.write('invalid format(missing "phy_resource"). ') - return False - - if not val.has_key('usage'): - sys.stderr.write('invalid format(missing "usage"). ') - return False - - if not self._check_format(val['phy_resource']): - return False - - for key, dic in val['usage'].items() : - if not self._check_format(dic): - return False - return True - - def _check_format(self, val): - if dict != type(val) : - sys.stderr.write('return value is not dict') - return False - - for key in ['vcpus', 'memory_mb', 'local_gb']: - if not val.has_key(key) : - sys.stderr.write('invalid format(missing "%s"). ' % key ) - return False - - return True - - - # ---> test for nova.scheduler.manager.show_host_resource() - - def test01(self): - """01: get NotFound exception when dest host not found on DB """ - - db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) - result = self.manager.show_host_resource(self.ctxt, 'not-registered-host') - c1 = ( not result['ret'] ) - c2 = ( 0 == result['msg'].find('No such') ) - self.assertEqual(c1 and c2, True) - - def test02(self): - """02: get other exception if unexpected err. """ - - db.host_get_by_name = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, self.manager.show_host_resource, self.ctxt, 'host1' ) - - def test03(self): - """03: no instance found on dest host. """ - - db.host_get_by_name = Mock( return_value = self.host1 ) - db.instance_get_all_by_host = Mock( return_value=[]) - ret= self.manager.show_host_resource(self.ctxt, 'host1') - - c1 = self.check_format(ret) - v = ret['phy_resource'] - c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) - c3 = ( 0 == len(ret['usage']) ) - - self.assertEqual(c1 and c2 and c3, True) - - def test04(self): - """04: some instance found on dest host. """ - - db.host_get_by_name = Mock( return_value = self.host1 ) - db.instance_get_all_by_host = Mock( return_value=[ self.instance1, - self.instance2, - self.instance3] ) - - db.instance_get_vcpu_sum_by_host_and_project = Mock(return_value=3) - db.instance_get_memory_sum_by_host_and_project = Mock(return_value=1024) - db.instance_get_disk_sum_by_host_and_project = Mock(return_value=5) - - ret= self.manager.show_host_resource(self.ctxt, 'host1') - - c1 = self.check_format(ret) - v = ret['phy_resource'] - c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) - c3 = ( 2 == len(ret['usage']) ) - c4 = ( self.instance1['project_id'] in ret['usage'].keys()) - c5 = ( self.instance3['project_id'] in ret['usage'].keys()) - - self.assertEqual(c1 and c2 and c3 and c4 and c5, True) - - - # ---> test for nova.scheduler.manager.has_enough_resource() - def test05(self): - """05: when cpu is exccded some instance found on dest host. """ - - db.instance_get = Mock(return_value = self.instance6) - try : - self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') - except exception.NotEmpty, e: - c1 = ( 0 < e.message.find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - - def test06(self): - """06: when memory is exccded some instance found on dest host. """ - - db.instance_get = Mock(return_value = self.instance7) - try : - self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') - except exception.NotEmpty, e: - c1 = ( 0 <= e.message.find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - def test07(self): - """07: when hdd is exccded some instance found on dest host. """ - - db.instance_get = Mock(return_value = self.instance8) - try : - self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') - except exception.NotEmpty, e: - c1 = ( 0 <= e.message.find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - - def test08(self): - """08: everything goes well. (instance_get_all_by_host returns list)""" - - ret= self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') - self.assertEqual(ret, None) - - - def test09(self): - """09: everything goes well(instance_get_all_by_host returns[]). """ - - db.instance_get_all_by_host = Mock(return_value = [] ) - ret= self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') - self.assertEqual(ret, None) - - - # ---> test for nova.scheduler.manager.live_migration() - - - def test10(self): - """10: instance_get_by_internal_id issue NotFound. """ - - # Mocks for has_enough_resource() - db.instance_get = Mock(return_value = self.instance8) - # Mocks for live_migration()db.instance_get_by_internal_id - # (any Mock is ok here. important mock is all above) - db.instance_get_by_internal_id = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'i-12345', - 'host1') - - - def test11(self): - """11: get NotFound exception when dest host not found on DB """ - - db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'i-12345', - 'host1') - - - def test12(self): - """12: Destination host is not compute node """ - self.assertRaises(exception.Invalid, - self.manager.live_migration, - self.ctxt, - 'i-12345', - 'host2') - - - # Cannot test the case of hypervisor type difference and hypervisor - # version difference, since we cannot set different mocks to same method.. - - def test13(self): - """13: rpc.call raises RemoteError(Unexpected error occurs when executing compareCPU) """ - rpc.call = Mock(return_value = rpc.RemoteError(libvirt.libvirtError, 'val', 'traceback')) - self.assertRaises(rpc.RemoteError, - self.manager.live_migration, - self.ctxt, - 'i-12345', - 'host1') - - def test14(self): - """14: rpc.call returns 0 (cpu is not compatible between src and dest) """ - rpc.call = Mock(return_value = 0) - try : - self.manager.live_migration(self.ctxt, 'i-12345', 'host1') - except exception.Invalid, e: - c1 = ( 0 <= e.message.find('doesnt have compatibility to')) - self.assertTrue(c1, True) - return False - - def test15(self): - """15: raise NotEmpty if host doesnt have enough resource. """ - - # Mocks for has_enough_resource() - db.instance_get = Mock(return_value = self.instance8) - - # Mocks for live_migration() - db.instance_get_by_internal_id = Mock(return_value = self.instance8) - db.instance_set_state = Mock(return_value = True) - rpc_cast = Mock(return_value = True) - - try : - self.manager.live_migration(self.ctxt, 'i-12345', 'host1') - except exception.NotEmpty, e: - c1 = ( 0 <= e.message.find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - - def test16(self): - """16: everything goes well. """ - - db.instance_get_by_internal_id = Mock(return_value = self.instance8) - db.instance_set_state = Mock(return_value = True) - rpc.cast = Mock(return_value = True) - - ret= self.manager.live_migration(self.ctxt, 'i-12345', 'host1') - self.assertEqual(ret, None) - - - def tearDown(self): - """common terminating method. """ - #sys.stdout = self.stdoutBak - pass - -if __name__ == '__main__': - #unittest.main() - suite = unittest.TestLoader().loadTestsFromTestCase(SchedulerTestFunctions) - unittest.TextTestRunner(verbosity=3).run(suite) - - ->>>>>>> MERGE-SOURCE diff --git a/nova/livemigration_test/UT/testCase_UT.xls b/nova/livemigration_test/UT/testCase_UT.xls deleted file mode 100644 index f73e8c5aa..000000000 Binary files a/nova/livemigration_test/UT/testCase_UT.xls and /dev/null differ -- cgit From a32ccff2e224d0d2bf72a0471d9e9599ba4d8990 Mon Sep 17 00:00:00 2001 From: masumotok Date: Fri, 24 Dec 2010 16:06:11 +0900 Subject: テスト項目表を再び追加した状態でコミット MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/livemigration_test/SI/picture.pptx | Bin 0 -> 137730 bytes nova/livemigration_test/SI/testCase_SI.xls | Bin 0 -> 43520 bytes .../SI/testParameterSheet_SI.xls | Bin 0 -> 464384 bytes nova/livemigration_test/SI/utils/demo-firstboot.sh | 39 ++ .../SI/utils/demo-runInstance.sh | 57 +++ nova/livemigration_test/SI/utils/nova-manage.conf | 18 + nova/livemigration_test/SI/utils/nova.conf | 10 + nova/livemigration_test/SI/utils/nova.sh | 180 +++++++++ nova/livemigration_test/SI/utils/nova.sh.compute | 37 ++ nova/livemigration_test/UT/computeManager.test.py | 411 +++++++++++++++++++++ .../UT/libvirtConnection.test.py | 372 +++++++++++++++++++ nova/livemigration_test/UT/nova-manage.test.py | 351 ++++++++++++++++++ .../livemigration_test/UT/schedulerManager.test.py | 408 ++++++++++++++++++++ nova/livemigration_test/UT/testCase_UT.xls | Bin 0 -> 202752 bytes 14 files changed, 1883 insertions(+) create mode 100644 nova/livemigration_test/SI/picture.pptx create mode 100644 nova/livemigration_test/SI/testCase_SI.xls create mode 100644 nova/livemigration_test/SI/testParameterSheet_SI.xls create mode 100755 nova/livemigration_test/SI/utils/demo-firstboot.sh create mode 100755 nova/livemigration_test/SI/utils/demo-runInstance.sh create mode 100644 nova/livemigration_test/SI/utils/nova-manage.conf create mode 100644 nova/livemigration_test/SI/utils/nova.conf create mode 100755 nova/livemigration_test/SI/utils/nova.sh create mode 100755 nova/livemigration_test/SI/utils/nova.sh.compute create mode 100644 nova/livemigration_test/UT/computeManager.test.py create mode 100644 nova/livemigration_test/UT/libvirtConnection.test.py create mode 100644 nova/livemigration_test/UT/nova-manage.test.py create mode 100644 nova/livemigration_test/UT/schedulerManager.test.py create mode 100644 nova/livemigration_test/UT/testCase_UT.xls (limited to 'nova') diff --git a/nova/livemigration_test/SI/picture.pptx b/nova/livemigration_test/SI/picture.pptx new file mode 100644 index 000000000..b47bec9b5 Binary files /dev/null and b/nova/livemigration_test/SI/picture.pptx differ diff --git a/nova/livemigration_test/SI/testCase_SI.xls b/nova/livemigration_test/SI/testCase_SI.xls new file mode 100644 index 000000000..65cf96fd7 Binary files /dev/null and b/nova/livemigration_test/SI/testCase_SI.xls differ diff --git a/nova/livemigration_test/SI/testParameterSheet_SI.xls b/nova/livemigration_test/SI/testParameterSheet_SI.xls new file mode 100644 index 000000000..400b43b43 Binary files /dev/null and b/nova/livemigration_test/SI/testParameterSheet_SI.xls differ diff --git a/nova/livemigration_test/SI/utils/demo-firstboot.sh b/nova/livemigration_test/SI/utils/demo-firstboot.sh new file mode 100755 index 000000000..3a6f7fb0b --- /dev/null +++ b/nova/livemigration_test/SI/utils/demo-firstboot.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +DIR=/opt/nova-2010.1 + +# 1. 管理者ユーザを作成する +# nova-manage user admin ユーザ名 access-key secret-key +# +#$DIR/bin/nova-manage user admin admin admin admin + +# 2. プロジェクトを作成する +# nova-manage create project プロジェクト名 プロジェクトに属するユーザ名 +# +#$DIR/bin/nova-manage project create admin admin + +# 3. クラウドを使うための認証情報を生成する +# nova-manage project environment プロジェクト名 ユーザ名 認証情報を格納するファイル +# +#$DIR/bin/nova-manage project environment admin admin $DIR/novarc + +# 4. 認証情報の読み込み +. $DIR/novarc + +# 5. プロジェクト用仮想マシンネットワークの作成を行う +# nova-manage user admin ユーザ名 access-key secret-key +# +$DIR/bin/nova-manage network create 10.0.0.0/8 3 16 + +# 6. 初回ログインにはSSHの公開鍵認証が必要 +# +if [ "" == "`euca-describe-keypairs | grep testkey`" ]; then + euca-add-keypair testkey > testkey.pem +fi + +# 7. +for i in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do + sudo ip addr del $i dev eth0 2> /dev/null +done + + diff --git a/nova/livemigration_test/SI/utils/demo-runInstance.sh b/nova/livemigration_test/SI/utils/demo-runInstance.sh new file mode 100755 index 000000000..171291262 --- /dev/null +++ b/nova/livemigration_test/SI/utils/demo-runInstance.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +DIR=/opt/nova-2010.1 + +function inc_assigned(){ + assigned=`expr $assigned + 1` +} + + +# 1. 認証情報の読み込み +. $DIR/novarc + +# 3. 仮想マシンの起動 +# +ret=`euca-run-instances -t m1.small -k testkey ami-centos` +#ret=`euca-run-instances -t m1.small -k testkey ami-tiny` + +# 4. 仮想マシン用IPの確保 +# 未登録なら登録しておく +registered=`euca-describe-addresses` +for ip in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do + + not_registered=`echo $registered | grep $ip` + if [ "" == "$not_registered" ]; then + echo "[INFO] registed $ip" + $DIR/bin/nova-manage floating create `hostname` $ip + fi +done + +# 5. IPの割当 +echo 0 > /tmp/demo-runinstance +euca-describe-addresses | grep -v reserved | while read line; do + # 割り当てられてないものを仮想マシンに割り当てる + ip=`echo $line | cut -d ' ' -f 2` + id=`echo $ret | cut -d ' ' -f 5` + if [ "" == "`echo $id | grep i- `" ] ; then + echo "[INFO] try again" $ret + break + fi + echo "[INFO] assigned to ipaddr($ip) to instance($id) " + euca-associate-address -i $id $ip + echo 1 > /tmp/demo-runinstance + break +done + +echo $assigned +if [ 0 -eq "`cat /tmp/demo-runinstance`" ] ; then + echo "[INFO] address is full." +fi +rm -rf /tmp/demo-runinstance + + +# 6. FWの設定 +euca-authorize -P tcp -p 22 default 2> /dev/null > /dev/null +euca-authorize -P tcp -p 80 default 2> /dev/null > /dev/null +euca-authorize -P tcp -p 5555 default 2> /dev/null > /dev/null + diff --git a/nova/livemigration_test/SI/utils/nova-manage.conf b/nova/livemigration_test/SI/utils/nova-manage.conf new file mode 100644 index 000000000..9f8a02b96 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova-manage.conf @@ -0,0 +1,18 @@ +--verbose +--nodaemon +--dhcpbridge_flagfile=/etc/nova/nova-manage.conf +--FAKE_subdomain=ec2 +--libvirt_type=qemu +--no_internet_conn=True +--public_netif=eth0 +--public_interface=eth0 + +--cc-host=172.19.0.131 +--routing_source_ip=172.19.0.131 +--sql_connection=mysql://root:nova@172.19.0.131/nova +--rabbit_host=172.19.0.131 +--redis_host=172.19.0.131 +--s3_host=172.19.0.131 +--auth_driver=nova.auth.ldapdriver.LdapDriver +--ldap_url=ldap://172.19.0.131 + diff --git a/nova/livemigration_test/SI/utils/nova.conf b/nova/livemigration_test/SI/utils/nova.conf new file mode 100644 index 000000000..c66bfbc53 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova.conf @@ -0,0 +1,10 @@ +--verbose +--nodaemon +--dhcpbridge_flagfile=/opt/nova-2010.4//bin/nova.conf +--network_manager=nova.network.manager.VlanManager +--cc_host=172.19.0.131 +--routing_source_ip=172.19.0.131 +--sql_connection=mysql://root:nova@localhost/nova +--auth_driver=nova.auth.ldapdriver.LdapDriver +--libvirt_type=qemu +--public_interface=eth0 diff --git a/nova/livemigration_test/SI/utils/nova.sh b/nova/livemigration_test/SI/utils/nova.sh new file mode 100755 index 000000000..b8e2e9f26 --- /dev/null +++ b/nova/livemigration_test/SI/utils/nova.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +DIR=`pwd` +CMD=$1 +SOURCE_BRANCH=lp:nova +if [ -n "$2" ]; then + SOURCE_BRANCH=$2 +fi +#DIRNAME=nova +DIRNAME="" +NOVA_DIR=$DIR/$DIRNAME +if [ -n "$3" ]; then + NOVA_DIR=$DIR/$3 +fi + +if [ ! -n "$HOST_IP" ]; then + # NOTE(vish): This will just get the first ip in the list, so if you + # have more than one eth device set up, this will fail, and + # you should explicitly set HOST_IP in your environment + HOST_IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` +fi + +#USE_MYSQL=${USE_MYSQL:-0} +USE_MYSQL=1 +MYSQL_PASS=${MYSQL_PASS:-nova} +TEST=${TEST:-0} +#USE_LDAP=${USE_LDAP:-0} +USE_LDAP=1 +LIBVIRT_TYPE=${LIBVIRT_TYPE:-qemu} +NET_MAN=${NET_MAN:-VlanManager} +# NOTE(vish): If you are using FlatDHCP on multiple hosts, set the interface +# below but make sure that the interface doesn't already have an +# ip or you risk breaking things. +# FLAT_INTERFACE=eth0 + +if [ "$USE_MYSQL" == 1 ]; then + SQL_CONN=mysql://root:$MYSQL_PASS@localhost/nova +else + SQL_CONN=sqlite:///$NOVA_DIR/nova.sqlite +fi + +if [ "$USE_LDAP" == 1 ]; then + AUTH=ldapdriver.LdapDriver +else + AUTH=dbdriver.DbDriver +fi + +mkdir -p /etc/nova +cat >$NOVA_DIR/bin/nova.conf << NOVA_CONF_EOF +--verbose +--nodaemon +--dhcpbridge_flagfile=$NOVA_DIR/bin/nova.conf +--network_manager=nova.network.manager.$NET_MAN +--cc_host=$HOST_IP +--routing_source_ip=$HOST_IP +--sql_connection=$SQL_CONN +--auth_driver=nova.auth.$AUTH +--libvirt_type=$LIBVIRT_TYPE +--public_interface=eth0 +NOVA_CONF_EOF + +if [ -n "$FLAT_INTERFACE" ]; then + echo "--flat_interface=$FLAT_INTERFACE" >>$NOVA_DIR/bin/nova.conf +fi + +if [ "$CMD" == "branch" ]; then + sudo apt-get install -y bzr + rm -rf $NOVA_DIR + bzr branch $SOURCE_BRANCH $NOVA_DIR + cd $NOVA_DIR + mkdir -p $NOVA_DIR/instances + mkdir -p $NOVA_DIR/networks +fi + +# You should only have to run this once +if [ "$CMD" == "install" ]; then + sudo apt-get install -y python-software-properties + sudo add-apt-repository ppa:nova-core/ppa + sudo apt-get update + sudo apt-get install -y dnsmasq kpartx kvm gawk iptables ebtables + sudo apt-get install -y user-mode-linux kvm libvirt-bin + sudo apt-get install -y screen euca2ools vlan curl rabbitmq-server + sudo apt-get install -y lvm2 iscsitarget open-iscsi + echo "ISCSITARGET_ENABLE=true" | sudo tee /etc/default/iscsitarget + sudo /etc/init.d/iscsitarget restart + sudo modprobe kvm + sudo /etc/init.d/libvirt-bin restart + sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot + sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy + sudo apt-get install -y python-libvirt python-libxml2 python-routes + if [ "$USE_MYSQL" == 1 ]; then + cat </etc/nova/nova-manage.conf << NOVA_CONF_EOF +--verbose +--nodaemon +--dhcpbridge_flagfile=/etc/nova/nova-manage.conf +--FAKE_subdomain=ec2 +--libvirt_type=qemu +--no_internet_conn=True +--public_netif=eth0 +--public_interface=eth0 + +--cc-host=$HOST_IP +--routing_source_ip=$HOST_IP +--sql_connection=mysql://root:nova@$HOST_IP/nova +--rabbit_host=$HOST_IP +--redis_host=$HOST_IP +--s3_host=$HOST_IP +--auth_driver=nova.auth.ldapdriver.LdapDriver +--ldap_url=ldap://$HOST_IP + +NOVA_CONF_EOF + +$DIR/bin/nova-compute --flagfile=/etc/nova/nova-manage.conf + diff --git a/nova/livemigration_test/UT/computeManager.test.py b/nova/livemigration_test/UT/computeManager.test.py new file mode 100644 index 000000000..69ee876d1 --- /dev/null +++ b/nova/livemigration_test/UT/computeManager.test.py @@ -0,0 +1,411 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + + +import sys +import os +import unittest +import commands +import re +import logging + +from mock import Mock +import twisted + +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + +try: + print + print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' \ + % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.compute.manager import ComputeManager + from nova.virt.libvirt_conn import LibvirtConnection + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set correct NOVA_DIR in this script. ' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + + def write(self, arg): + self.buffer += arg + + def writelines(self, arg): + self.buffer += arg + + def flush(self): + print 'flush' + self.buffer = '' + + +class tmpStderr(tmpStdout): + def write(self, arg): + self.buffer += arg + + def flush(self): + pass + + def realFlush(self): + self.buffer = '' + +dummyCallReturnValue={ 0:True } +dummyCallCount=0 +def dummyCall(context, topic, method): + global dummyCallReturnValue, dummyCallCount + if dummyCallCount in dummyCallReturnValue.keys() : + ret = dummyCallReturnValue[ dummyCallCount ] + dummyCallCount += 1 + return ret + else : + dummyCallCount += 1 + return False + + +class ComputeTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + stderr = None + stderrBak = None + manager = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + #if self.stdout is None: + # self.__class__.stdout = tmpStdout() + #self.stdoutBak = sys.stdout + #sys.stdout = self.stdout + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + + self.host = 'openstack2-api' + if self.manager is None: + self.__class__.manager = ComputeManager(host=self.host) + + self.setTestData() + self.setMocks() + + def setTestData(self): + + self.host1 = Host() + for key, val in [('name', 'host1'), ('cpu', 5), + ('memory_mb', 20480), ('hdd_gb', 876)]: + self.host1.__setitem__(key, val) + + self.host2 = Host() + for key, val in [('name', 'host2'), ('cpu', 5), + ('memory_mb', 20480), ('hdd_gb', 876)]: + self.host2.__setitem__(key, val) + + self.instance1 = Instance() + for key, val in [('id', 1), ('host', 'host1'), + ('hostname', 'i-12345'), ('state', power_state.RUNNING), + ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), + ('hdd_gb', 5), ('internal_id', 12345)]: + self.instance1.__setitem__(key, val) + + self.instance2 = Instance() + for key, val in [('id', 2), ('host', 'host1'), + ('hostname', 'i-12345'), ('state', power_state.RUNNING), + ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), + ('hdd_gb', 5)]: + self.instance2.__setitem__(key, val) + + self.fixed_ip1 = FixedIp() + for key, val in [('id', 1), ('address', '1.1.1.1'), + ('network_id', '1'), ('instance_id', 1)]: + self.fixed_ip1.__setitem__(key, val) + + self.vol1 = Volume() + for key, val in [('id', 1), ('ec2_id', 'vol-qijjuc7e'), + ('availability_zone', 'nova'), ('host', 'host1')]: + self.vol1.__setitem__(key, val) + + self.vol2 = Volume() + for key, val in [('id', 2), ('ec2_id', 'vol-qi22222'), + ('availability_zone', 'nova'), ('host', 'host1')]: + self.vol2.__setitem__(key, val) + + self.secgrp1 = Volume() + for key, val in [('id', 1), ('ec2_id', 'default')]: + self.secgrp1.__setitem__(key, val) + + self.secgrp2 = Volume() + for key, val in [('id', 2), ('ec2_id', 'def2')]: + self.secgrp2.__setitem__(key, val) + + self.netref1 = Network() + + def setMocks(self): + + # mocks for pre_live_migration + self.ctxt = context.get_admin_context() + db.instance_get = Mock(return_value=self.instance1) + db.volume_get_by_ec2_id = Mock(return_value=[self.vol1, self.vol2]) + db.volume_get_shelf_and_blade = Mock(return_value=(3, 4)) + db.instance_get_fixed_address = Mock(return_value=self.fixed_ip1) + db.security_group_get_by_instance \ + = Mock(return_value=[self.secgrp1, self.secgrp2]) + self.manager.driver.setup_nwfilters_for_instance \ + = Mock(return_value=None) + self.manager.driver.nwfilter_for_instance_exists = Mock(return_value=None) + self.manager.network_manager.setup_compute_network \ + = Mock(return_value=None) + # mocks for live_migration_ + rpc.call = Mock(return_value=True) + db.instance_set_state = Mock(return_value=True) + + # ---> test for nova.compute.manager.pre_live_migration() + def test01(self): + """01: NotFound error occurs on finding instance on DB. """ + + db.instance_get = Mock(side_effect=exception.NotFound('ERR')) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test02(self): + """02: NotAuthrized occurs on finding volume on DB. """ + + db.volume_get_by_ec2_id \ + = Mock(side_effect=exception.NotAuthorized('ERR')) + + self.assertRaises(exception.NotAuthorized, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test03(self): + """03: Unexpected exception occurs on finding volume on DB. """ + + db.volume_get_by_ec2_id = Mock(side_effect=TypeError('ERR')) + + self.assertRaises(TypeError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test04(self): + """04: no volume and fixed ip found on DB, """ + + db.volume_get_by_ec2_id = Mock(side_effect=exception.NotFound('ERR')) + db.instance_get_fixed_address = Mock(return_value=None) + + self.assertRaises(rpc.RemoteError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + c1 = (0 <= sys.stderr.buffer.find('has no volume')) + + self.assertEqual(c1, True) + + def test05(self): + """05: volume found and no fixed_ip found on DB. """ + + db.instance_get_fixed_address \ + = Mock(side_effect=exception.NotFound('ERR')) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test06(self): + """06: self.driver.setup_nwfilters_for_instance causes NotFound. """ + self.manager.driver.setup_nwfilters_for_instance \ + = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test07(self): + """07: self.network_manager.setup_compute_network causes ProcessExecutionError. """ + self.manager.network_manager.setup_compute_network \ + = Mock(side_effect=exception.ProcessExecutionError("ERR")) + + self.assertRaises(exception.ProcessExecutionError, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + + def test08(self): + """08: self.manager.network_manager.setup_compute_network + exception.NotFound. """ + self.manager.network_manager.setup_compute_network \ + = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.pre_live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + # those 2 cases are omitted : + # self.driver.setup_nwfilters_for_instance causes + # twisted.python.failure.Failure. + # self.driver.refresh_security_group causes twisted.python.failure.Failure. + # + # twisted.python.failure.Failure can not be used with assertRaises, + # it doesnt have __call___ + # + + def test09(self): + """09: volume/fixed_ip found on DB, all procedure finish + successfully.. """ + + result = self.manager.pre_live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + self.assertEqual(result, True) + + # ---> test for nova.compute.manager.live_migration() + + def test10(self): + """10: rpc.call(pre_live_migration returns Error(Not None). """ + rpc.call = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test11(self): + """11: if rpc.call returns rpc.RemoteError. """ + + rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) + db.instance_set_state = Mock(return_value=True) + result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + c1 = (None == result) + c2 = (0 <= sys.stderr.buffer.find('err at')) + self.assertEqual(c1 and c2, True) + + def test12(self): + """12: if rpc.call returns rpc.RemoteError and instance_set_state + also ends up err. (then , unexpected err occurs, in this case + TypeError) + """ + rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) + db.instance_set_state = Mock(side_effect=TypeError("ERR")) + self.assertRaises(TypeError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test13(self): + """13: if wait for pre_live_migration, but timeout. """ + rpc.call = dummyCall + + db.instance_get = Mock(return_value=self.instance1) + + result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', + 'host2') + c1 = (None == result) + c2 = (0 <= sys.stderr.buffer.find('Timeout for')) + self.assertEqual(c1 and c2, True) + + def test14(self): + """14: if db_instance_get issues NotFound. + """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(side_effect=exception.NotFound("ERR")) + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test15(self): + """15: if rpc.call returns True, and instance_get() cause other + exception. (Unexpected case - b/c it already checked by + nova-manage) + """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(side_effect=TypeError("ERR")) + + self.assertRaises(TypeError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test16(self): + """16: if rpc.call returns True, and live_migration issues + ProcessExecutionError. """ + rpc.call = Mock(return_value=True) + db.instance_get = Mock(return_value=self.instance1) + ret = self.manager.driver.live_migration \ + = Mock(side_effect=utils.ProcessExecutionError("ERR")) + + self.assertRaises(utils.ProcessExecutionError, + self.manager.live_migration, + self.ctxt, + 'dummy_ec2_id', + 'host2') + + def test17(self): + """17: everything goes well. """ + self.manager.driver.live_migration = Mock(return_value=True) + ret = self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + self.assertEqual(True, True) + + def tearDown(self): + """common terminating method. """ + self.stderr.realFlush() + sys.stderr = self.stderrBak + #sys.stdout = self.stdoutBak + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.DEBUG) + #unittest.main() + + suite = unittest.TestLoader().loadTestsFromTestCase(ComputeTestFunctions) + unittest.TextTestRunner(verbosity=2).run(suite) + + #suite = unittest.TestSuite() + #suite.addTest(ComputeTestFunctions("test15")) + #suite.addTest(ComputeTestFunctions("test16")) + #unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/nova/livemigration_test/UT/libvirtConnection.test.py b/nova/livemigration_test/UT/libvirtConnection.test.py new file mode 100644 index 000000000..5dfe8702c --- /dev/null +++ b/nova/livemigration_test/UT/libvirtConnection.test.py @@ -0,0 +1,372 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + + +import sys +import os +import unittest +import commands +import re +import logging +import libvirt + +from mock import Mock +import twisted + +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + + +try : + print + print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.compute.manager import ComputeManager + from nova.virt import libvirt_conn + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova import process + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set correct NOVA_DIR in this script. ' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def writelines(self, arg): + self.buffer += arg + def flush(self): + print 'flush' + self.buffer = '' + +class tmpStderr(tmpStdout): + def write(self,arg): + self.buffer += arg + def flush(self): + pass + def realFlush(self): + self.buffer = '' + +class DummyLibvirtConn(object): + nwfilterLookupByName = None + def __init__(self): + pass + + +class LibvirtConnectionTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + stderr = None + stderrBak = None + manager = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + #if self.stdout is None: + # self.__class__.stdout = tmpStdout() + #self.stdoutBak = sys.stdout + #sys.stdout = self.stdout + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + + self.host = 'openstack2-api' + if self.manager is None: + self.__class__.manager = libvirt_conn.get_connection(False) + + self.setTestData() + self.setMocks() + + def setTestData(self): + + self.host1 = Host() + for key, val in [ ('name', 'host1'), ('cpu', 5), ('memory_mb', 20480), ('hdd_gb', 876) ]: + self.host1.__setitem__(key, val) + + self.instance1 = Instance() + for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5), ('internal_id',12345) ]: + self.instance1.__setitem__(key, val) + + + self.instance2 = Instance() + for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance2.__setitem__(key, val) + + + self.fixed_ip1 = FixedIp() + for key, val in [ ('id', 1), ('address', '1.1.1.1'), ('network_id', '1'), + ('instance_id', 1)]: + self.fixed_ip1.__setitem__(key, val) + + self.floating_ip1 = FloatingIp() + for key, val in [ ('id', 1), ('address', '1.1.1.200') ]: + self.floating_ip1.__setitem__(key, val) + + self.netref1 = Network() + for key, val in [ ('id', 1) ]: + self.netref1.__setitem__(key, val) + + + def setMocks(self): + + self.ctxt = context.get_admin_context() + db.instance_get_fixed_address = Mock(return_value = '1.1.1.1') + db.fixed_ip_update = Mock(return_value = None) + db.fixed_ip_get_network = Mock(return_value = self.netref1) + db.network_update = Mock(return_value = None) + db.instance_get_floating_address = Mock(return_value = '1.1.1.200') + db.floating_ip_get_by_address = Mock(return_value = self.floating_ip1) + db.floating_ip_update = Mock(return_value = None) + db.instance_update = Mock(return_value = None) + + + # ---> test for nova.virt.libvirt_conn.nwfilter_for_instance_exists() + + def test01(self): + """01: libvirt.libvirtError occurs. """ + + self.manager._wrapped_conn = DummyLibvirtConn() + self.manager._test_connection = Mock(return_value=True) + self.manager._conn.nwfilterLookupByName = \ + Mock(side_effect=libvirt.libvirtError("ERR")) + ret = self.manager.nwfilter_for_instance_exists(self.instance1) + self.assertEqual(ret, False) + + def test02(self): + """02: libvirt.libvirtError not occurs. """ + + self.manager._wrapped_conn = DummyLibvirtConn() + self.manager._test_connection = Mock(return_value=True) + self.manager._conn.nwfilterLookupByName = \ + Mock(return_value=True) + ret = self.manager.nwfilter_for_instance_exists(self.instance1) + self.assertEqual(ret, True) + + # ---> test for nova.virt.libvirt_conn.live_migraiton() + + def test03(self): + """03: Unexpected exception occurs on finding volume on DB. """ + + utils.execute = Mock( side_effect=process.ProcessExecutionError('ERR') ) + + self.assertRaises(process.ProcessExecutionError, + self.manager.live_migration, + self.instance1, + 'host2') + + # ---> other case cannot be tested because live_migraiton + # is synchronized/asynchronized method are mixed together + + + # ---> test for nova.virt.libvirt_conn._post_live_migraiton + + def test04(self): + """04: instance_ref is not nova.db.sqlalchemy.models.Instances""" + + self.assertRaises(TypeError, + self.manager._post_live_migration, + "dummy string", + 'host2') + + def test05(self): + """05: db.instance_get_fixed_address return None""" + + db.instance_get_fixed_address = Mock( return_value=None ) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('fixed_ip is not found')) + self.assertEqual(c1 and c2, True) + + def test06(self): + """06: db.instance_get_fixed_address raises NotFound""" + + db.instance_get_fixed_address = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host2') + + def test07(self): + """07: db.instance_get_fixed_address raises Unknown exception""" + + db.instance_get_fixed_address = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test08(self): + """08: db.fixed_ip_update return NotFound. """ + + db.fixed_ip_update = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test09(self): + """09: db.fixed_ip_update return NotAuthorized. """ + db.fixed_ip_update = Mock( side_effect=exception.NotAuthorized('ERR') ) + self.assertRaises(exception.NotAuthorized, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test10(self): + """10: db.fixed_ip_update return Unknown exception. """ + db.fixed_ip_update = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test11(self): + """11: db.fixed_ip_get_network causes NotFound. """ + + db.fixed_ip_get_network = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager._post_live_migration, + self.instance1, + 'host1') + + # not tested db.fixed_ip_get_network raises NotAuthorized + # because same test has been done at previous test. + + def test12(self): + """12: db.fixed_ip_get_network causes Unknown exception. """ + + db.fixed_ip_get_network = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test13(self): + """13: db.network_update raises Unknown exception. """ + db.network_update = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def test14(self): + """14: db.instance_get_floating_address raises NotFound. """ + db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + + def test15(self): + """15: db.instance_get_floating_address returns None. """ + + db.instance_get_floating_address = Mock( return_value=None ) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('floating_ip is not found')) + self.assertEqual(c1 and c2, True) + + def test16(self): + """16: db.instance_get_floating_address raises NotFound. """ + + db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + def test17(self): + """17: db.instance_get_floating_address raises Unknown exception. """ + db.instance_get_floating_address = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + + def test18(self): + """18: db.floating_ip_get_by_address raises NotFound """ + + db.floating_ip_get_by_address = Mock(side_effect=exception.NotFound("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) + self.assertEqual(c1 and c2, True) + + def test19(self): + """19: db.floating_ip_get_by_address raises Unknown exception. """ + db.floating_ip_get_by_address = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + + def test20(self): + """20: db.floating_ip_update raises Unknown exception. + """ + db.floating_ip_update = Mock(side_effect=TypeError("ERR")) + ret = self.manager._post_live_migration(self.instance1, 'host1') + c1 = (ret == None) + c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) + self.assertEqual(c1 and c2, True) + + def test21(self): + """21: db.instance_update raises unknown exception. """ + + db.instance_update = Mock(side_effect=TypeError("ERR")) + self.assertRaises(TypeError, + self.manager._post_live_migration, + self.instance1, + 'host1') + + def tearDown(self): + """common terminating method. """ + self.stderr.realFlush() + sys.stderr = self.stderrBak + #sys.stdout = self.stdoutBak + +if __name__ == '__main__': + logging.getLogger().setLevel(logging.DEBUG) + #unittest.main() + + suite = unittest.TestLoader().loadTestsFromTestCase(LibvirtConnectionTestFunctions) + unittest.TextTestRunner(verbosity=2).run(suite) + + #suite = unittest.TestSuite() + #suite.addTest(LibvirtConnectionTestFunctions("test14")) + #suite.addTest(LibvirtConnectionTestFunctions("test16")) + #unittest.TextTestRunner(verbosity=2).run(suite) + + diff --git a/nova/livemigration_test/UT/nova-manage.test.py b/nova/livemigration_test/UT/nova-manage.test.py new file mode 100644 index 000000000..f1653d21a --- /dev/null +++ b/nova/livemigration_test/UT/nova-manage.test.py @@ -0,0 +1,351 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +NOVA_DIR='/opt/nova-2010.4' + +import sys +import os +import unittest +import commands +import re + +from mock import Mock + +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + + +try : + print + print 'Testing %s/bin/nova-manage, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + + +except: + print 'set correct NOVA_DIR in this script. ' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def flush(self): + self.buffer = '' + +class tmpStderr(tmpStdout): + def write(self, arg): + self.buffer += arg + def flush(self): + pass + def realFlush(self): + self.buffer = '' + + +class NovaManageTestFunctions(unittest.TestCase): + + stdout = None + stdoutBak = None + stderr = None + stderrBak = None + + hostCmds = None + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + commands.getstatusoutput('cp -f %s/bin/nova-manage %s' % ( NOVA_DIR, self.getNovaManageCopyPath() )) + commands.getstatusoutput('touch %s' % self.getInitpyPath() ) + try : + import bin.novamanagetest + except: + print 'Fail to import nova-manage . check bin/nova-manage exists' + raise + + # replace stdout for checking nova-manage output + if self.stdout is None : + self.__class__.stdout = tmpStdout() + self.stdoutBak = sys.stdout + sys.stdout = self.stdout + + # replace stderr for checking nova-manage output + if self.stderr is None: + self.__class__.stderr = tmpStderr() + self.stderrBak = sys.stderr + sys.stderr = self.stderr + + # prepare test data + self.setTestData() + + + def setTestData(self): + import bin.novamanagetest + + if self.hostCmds is None : + self.__class__.hostCmds = bin.novamanagetest.HostCommands() + self.instanceCmds = bin.novamanagetest.InstanceCommands() + + self.host1 = Host() + self.host1.__setitem__('name', 'host1') + + self.host2 = Host() + self.host2.__setitem__('name', 'host2') + + self.instance1 = Instance() + self.instance1.__setitem__('id', 1) + self.instance1.__setitem__('host', 'host1') + self.instance1.__setitem__('hostname', 'i-12345') + self.instance1.__setitem__('state', power_state.NOSTATE) + self.instance1.__setitem__('state_description', 'running') + + self.instance2 = Instance() + self.instance2.__setitem__('id', 2) + self.instance2.__setitem__('host', 'host1') + self.instance2.__setitem__('hostname', 'i-12345') + self.instance2.__setitem__('state', power_state.RUNNING) + self.instance2.__setitem__('state_description', 'pending') + + self.instance3 = Instance() + self.instance3.__setitem__('id', 3) + self.instance3.__setitem__('host', 'host1') + self.instance3.__setitem__('hostname', 'i-12345') + self.instance3.__setitem__('state', power_state.RUNNING) + self.instance3.__setitem__('state_description', 'running') + + db.host_get_all = Mock(return_value=[self.host1, self.host2]) + + def getInitpyPath(self): + return '%s/bin/__init__.py' % NOVA_DIR + + def getNovaManageCopyPath(self): + return '%s/bin/novamanagetest.py' % NOVA_DIR + + # -----> Test for nova-manage host list + + def test01(self): + """01: Got some host lists. """ + + self.hostCmds.list() + + c1 = (2 == self.stdout.buffer.count('\n')) + c2 = (0 <= self.stdout.buffer.find('host1')) + c3 = (0 <= self.stdout.buffer.find('host2')) + self.assertEqual(c1 and c2 and c3, True) + + def test02(self): + """02: Got empty lsit. """ + + db.host_get_all = Mock(return_value=[]) + self.hostCmds.list() + + # result should be empty + c = (0 == len(self.stdout.buffer) ) + self.assertEqual(c, True) + + def test03(self): + """03: Got notFound """ + + db.host_get_all = Mock(side_effect=exception.NotFound("ERR")) + self.assertRaises(exception.NotFound, self.hostCmds.list) + + # --------> Test For nova-manage host show + + def test04(self): + """04: args are not enough(nova-manage host show) """ + self.assertRaises(TypeError, self.hostCmds.show ) + + + def test05(self): + """05: nova-manage host show not-registered-host, and got an error""" + + rpc.call = Mock(return_value={'ret' : False, 'msg': 'ERR'} ) + self.hostCmds.show('host1') + self.assertEqual( self.stdout.buffer[:3]=='ERR', True ) + + + def test06(self): + """06: nova-manage host show registerd-host, and no project uses the host""" + + dic = {'ret': True, + 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, + 'usage': {}} + + rpc.call = Mock(return_value=dic ) + self.hostCmds.show('host1') + + # result should be : + # HOST PROJECT cpu mem(mb) disk(gb) + # host1 1 2 3 + line = self.stdout.buffer.split('\n')[1] + line = re.compile('\t+').sub(' ', line).strip() + c1 = ( 'host1 1 2 3' == line ) + c2 = ( self.stdout.buffer.count('\n') == 2 ) + + self.assertEqual( c1 and c2, True ) + + def test07(self): + """07: nova-manage host show registerd-host, + and some projects use the host + """ + dic = {'ret': True, + 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, + 'usage': {'p1': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, + 'p2': {'vcpus':1, 'memory_mb':2, 'local_gb':3} }} + + rpc.call = Mock(return_value=dic ) + self.hostCmds.show('host1') + + # result should be : + # HOST PROJECT cpu mem(mb) disk(gb) + # host1 1 2 3 + # host1 p1 1 2 3 + # host1 p2 4 5 6 + line = self.stdout.buffer.split('\n')[1] + ret = re.compile('\t+').sub(' ', line).strip() + c1 = ( 'host1 1 2 3' == ret ) + + line = self.stdout.buffer.split('\n')[2] + line = re.compile('\t+').sub(' ', line).strip() + c2 = ( 'host1 p1 1 2 3' == line ) or ( 'host1 p2 1 2 3' == line ) + + line = self.stdout.buffer.split('\n')[3] + ret = re.compile('\t+').sub(' ', line).strip() + c3 = ( 'host1 p1 1 2 3' == ret ) or ( 'host1 p2 1 2 3' == ret ) + + self.assertEqual( c1 and c2 and c3, True ) + + def test08(self): + """08: nova-manage host show registerd-host, and rpc.call returns None + (unexpected error) + """ + rpc.call = Mock(return_value=None ) + self.hostCmds.show('host1') + c1 = ( 0 <= self.stdout.buffer.find('Unexpected error') ) + self.assertEqual( c1, True ) + + # ----------> Test for bin/nova-manage instance live_migration + + def test09(self): + """09: arguments are not enough(nova-manage instances live_migration) + """ + self.assertRaises(TypeError, self.instanceCmds.live_migration ) + + def test10(self): + """10: arguments are not enough(nova-manage instances live_migration ec2_id) + """ + self.assertRaises(TypeError, self.instanceCmds.live_migration, 'i-xxx' ) + + def test11(self): + """11: nova-manage instances live_migration ec2_id host, + where hostname is invalid + """ + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) + + def test12(self): + """12: nova-manage instances live_migration ec2_id(invalid id) host""" + + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( side_effect=exception.NotFound('ERR') ) + + self.assertRaises(exception.NotFound, self.instanceCmds.live_migration, 'i-xxx', 'host1' ) + + def test13(self): + """13: nova-manage instances live_migration ec2_id host, + but instance specifed by ec2 id is not running (state is not power_state.RUNNING) + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) + try : + self.instanceCmds.live_migration('i-12345', 'host1') + except exception.Invalid, e: + c1 = (0 < e.message.find('is not running') ) + self.assertTrue(c1, True) + return False + + + def test14(self): + """14: nova-manage instances live_migration ec2_id host, + but instance specifed by ec2 id is not running (state_description is not running) + """ + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_by_internal_id = Mock( return_value = self.instance1 ) + try : + self.instanceCmds.live_migration('i-12345', 'host2') + except exception.Invalid, e: + c1 = (0 < e.message.find('is not running') ) + self.assertTrue(c1, True) + return False + + def test15(self): + """15: nova-manage instances live_migration ec2_id host, + but instance is running at the same host specifed above, so err should be occured. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + try : + self.instanceCmds.live_migration('i-12345', 'host1') + except exception.Invalid, e: + c1 = ( 0 <= e.message.find('is running now') ) + self.assertTrue(c1, True) + return False + + + def test16(self): + """16: nova-manage instances live_migration ec2_id host, + rpc.call raises RemoteError because destination doesnt have enough resource. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + rpc.call = Mock(return_value = rpc.RemoteError(TypeError, 'val', 'traceback')) + self.assertRaises(rpc.RemoteError, self.instanceCmds.live_migration, 'i-xxx', 'host2' ) + + + def test17(self): + """17: nova-manage instances live_migration ec2_id host, + everything goes well, ang gets success messages. + """ + db.host_get_by_name = Mock(return_value = self.host1) + db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) + rpc.call = Mock(return_value = None) + + self.instanceCmds.live_migration('i-12345', 'host2') + c1 = (0 <= self.stdout.buffer.find('Finished all procedure') ) + self.assertEqual( c1, True ) + + + def tearDown(self): + """common terminating method. """ + commands.getstatusoutput('rm -rf %s' % self.getInitpyPath() ) + commands.getstatusoutput('rm -rf %s' % self.getNovaManageCopyPath() ) + sys.stdout.flush() + sys.stdout = self.stdoutBak + self.stderr.realFlush() + sys.stderr = self.stderrBak + +if __name__ == '__main__': + #unittest.main() + suite = unittest.TestLoader().loadTestsFromTestCase(NovaManageTestFunctions) + unittest.TextTestRunner(verbosity=3).run(suite) + + diff --git a/nova/livemigration_test/UT/schedulerManager.test.py b/nova/livemigration_test/UT/schedulerManager.test.py new file mode 100644 index 000000000..a0b76c918 --- /dev/null +++ b/nova/livemigration_test/UT/schedulerManager.test.py @@ -0,0 +1,408 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- + + +import sys +import os +import unittest +import commands +import re +import libvirt + +from mock import Mock + +# getting /nova-inst-dir +NOVA_DIR = os.path.abspath(sys.argv[0]) +for i in range(4): + NOVA_DIR = os.path.dirname(NOVA_DIR) + +try : + print + print 'Checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR + print + + sys.path.append(NOVA_DIR) + + from nova.scheduler.manager import SchedulerManager + + from nova import context + from nova import db + from nova import exception + from nova import flags + from nova import quota + from nova import utils + from nova.auth import manager + from nova.cloudpipe import pipelib + from nova import rpc + from nova.api.ec2 import cloud + from nova.compute import power_state + + from nova.db.sqlalchemy.models import * + +except: + print 'set correct NOVA_DIR in this script. ' + raise + + +class tmpStdout: + def __init__(self): + self.buffer = "" + def write(self,arg): + self.buffer += arg + def flush(self): + self.buffer = '' + + +class SchedulerTestFunctions(unittest.TestCase): + + # 共通の初期化処理 + def setUp(self): + """common init method. """ + + self.host = 'openstack2-api' + self.manager = SchedulerManager(host=self.host) + + self.setTestData() + self.setMocks() + + def setTestData(self): + + self.host1 = Host() + self.host1.__setitem__('name', 'host1') + self.host1.__setitem__('vcpus', 5) + self.host1.__setitem__('memory_mb', 20480) + self.host1.__setitem__('local_gb', 876) + + self.host2 = Host() + self.host2.__setitem__('name', 'host2') + self.host2.__setitem__('vcpus', 5) + self.host2.__setitem__('memory_mb', 20480) + self.host2.__setitem__('local_gb', 876) + self.host2.__setitem__('hypervisor_type', 'QEMU') + self.host2.__setitem__('hypervisor_version', 12003) + xml="x86_64NehalemIntel" + self.host2.__setitem__('cpu_info', xml) + + self.instance1 = Instance() + for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance1.__setitem__(key, val) + + + self.instance2 = Instance() + for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ'), + ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance2.__setitem__(key, val) + + + self.instance3 = Instance() + for key, val in [ ('id', 3), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('hdd_gb', 5) ]: + self.instance3.__setitem__(key, val) + + self.instance4 = Instance() + for key, val in [ ('id', 4), ('host', 'host2'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance4.__setitem__(key, val) + + self.instance5 = Instance() + for key, val in [ ('id', 5), ('host', 'host2'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance5.__setitem__(key, val) + + self.instance6 = Instance() + for key, val in [ ('id', 6), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: + self.instance6.__setitem__(key, val) + + self.instance7 = Instance() + for key, val in [ ('id', 7), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 18432), ('local_gb', 5) ]: + self.instance7.__setitem__(key, val) + + self.instance8 = Instance() + for key, val in [ ('id', 8), ('host', 'host1'), ('hostname', 'i-12345'), + ('state', power_state.RUNNING), ('project_id', 'testPJ2'), + ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 866) ]: + self.instance8.__setitem__(key, val) + + self.service1 = Service() + for key, val in [ ('id', 1), ('host', 'host1'), ('binary', 'nova-compute'), + ('topic', 'compute')]: + self.service1.__setitem__(key, val) + + + def setMocks(self): + self.ctxt = context.get_admin_context() + # Mocks for has_enough_resource() + db.instance_get = Mock(return_value = self.instance3) + db.host_get_by_name = Mock(return_value = self.host2) + db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) + + # Mocks for live_migration + db.instance_get_by_internal_id = Mock(return_value = self.instance1) + # db.host_get_by_name <- defined above. + db.service_get_all_by_topic = Mock(return_value = [self.service1] ) + rpc.call = Mock(return_value=1) + + def check_format(self, val): + """check result format of show_host_resource """ + + if dict != type(val) : + sys.stderr.write('return value is not dict') + return False + + if not val.has_key('ret'): + sys.stderr.write('invalid format(missing "ret"). ') + return False + + if not val['ret'] : + if not val.has_key('msg') : + sys.stderr.write( 'invalid format(missing "msg").' ) + return False + + else : + if not val.has_key('phy_resource') : + sys.stderr.write('invalid format(missing "phy_resource"). ') + return False + + if not val.has_key('usage'): + sys.stderr.write('invalid format(missing "usage"). ') + return False + + if not self._check_format(val['phy_resource']): + return False + + for key, dic in val['usage'].items() : + if not self._check_format(dic): + return False + return True + + def _check_format(self, val): + if dict != type(val) : + sys.stderr.write('return value is not dict') + return False + + for key in ['vcpus', 'memory_mb', 'local_gb']: + if not val.has_key(key) : + sys.stderr.write('invalid format(missing "%s"). ' % key ) + return False + + return True + + + # ---> test for nova.scheduler.manager.show_host_resource() + + def test01(self): + """01: get NotFound exception when dest host not found on DB """ + + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + result = self.manager.show_host_resource(self.ctxt, 'not-registered-host') + c1 = ( not result['ret'] ) + c2 = ( 0 == result['msg'].find('No such') ) + self.assertEqual(c1 and c2, True) + + def test02(self): + """02: get other exception if unexpected err. """ + + db.host_get_by_name = Mock( side_effect=TypeError('ERR') ) + self.assertRaises(TypeError, self.manager.show_host_resource, self.ctxt, 'host1' ) + + def test03(self): + """03: no instance found on dest host. """ + + db.host_get_by_name = Mock( return_value = self.host1 ) + db.instance_get_all_by_host = Mock( return_value=[]) + ret= self.manager.show_host_resource(self.ctxt, 'host1') + + c1 = self.check_format(ret) + v = ret['phy_resource'] + c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) + c3 = ( 0 == len(ret['usage']) ) + + self.assertEqual(c1 and c2 and c3, True) + + def test04(self): + """04: some instance found on dest host. """ + + db.host_get_by_name = Mock( return_value = self.host1 ) + db.instance_get_all_by_host = Mock( return_value=[ self.instance1, + self.instance2, + self.instance3] ) + + db.instance_get_vcpu_sum_by_host_and_project = Mock(return_value=3) + db.instance_get_memory_sum_by_host_and_project = Mock(return_value=1024) + db.instance_get_disk_sum_by_host_and_project = Mock(return_value=5) + + ret= self.manager.show_host_resource(self.ctxt, 'host1') + + c1 = self.check_format(ret) + v = ret['phy_resource'] + c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) + c3 = ( 2 == len(ret['usage']) ) + c4 = ( self.instance1['project_id'] in ret['usage'].keys()) + c5 = ( self.instance3['project_id'] in ret['usage'].keys()) + + self.assertEqual(c1 and c2 and c3 and c4 and c5, True) + + + # ---> test for nova.scheduler.manager.has_enough_resource() + def test05(self): + """05: when cpu is exccded some instance found on dest host. """ + + db.instance_get = Mock(return_value = self.instance6) + try : + self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 < e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False + + + def test06(self): + """06: when memory is exccded some instance found on dest host. """ + + db.instance_get = Mock(return_value = self.instance7) + try : + self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 <= e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False + + def test07(self): + """07: when hdd is exccded some instance found on dest host. """ + + db.instance_get = Mock(return_value = self.instance8) + try : + self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 <= e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False + + + def test08(self): + """08: everything goes well. (instance_get_all_by_host returns list)""" + + ret= self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + self.assertEqual(ret, None) + + + def test09(self): + """09: everything goes well(instance_get_all_by_host returns[]). """ + + db.instance_get_all_by_host = Mock(return_value = [] ) + ret= self.manager.has_enough_resource(self.ctxt, 'i-12345', 'host1') + self.assertEqual(ret, None) + + + # ---> test for nova.scheduler.manager.live_migration() + + + def test10(self): + """10: instance_get_by_internal_id issue NotFound. """ + + # Mocks for has_enough_resource() + db.instance_get = Mock(return_value = self.instance8) + # Mocks for live_migration()db.instance_get_by_internal_id + # (any Mock is ok here. important mock is all above) + db.instance_get_by_internal_id = Mock(side_effect=exception.NotFound("ERR")) + + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'i-12345', + 'host1') + + + def test11(self): + """11: get NotFound exception when dest host not found on DB """ + + db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) + self.assertRaises(exception.NotFound, + self.manager.live_migration, + self.ctxt, + 'i-12345', + 'host1') + + + def test12(self): + """12: Destination host is not compute node """ + self.assertRaises(exception.Invalid, + self.manager.live_migration, + self.ctxt, + 'i-12345', + 'host2') + + + # Cannot test the case of hypervisor type difference and hypervisor + # version difference, since we cannot set different mocks to same method.. + + def test13(self): + """13: rpc.call raises RemoteError(Unexpected error occurs when executing compareCPU) """ + rpc.call = Mock(return_value = rpc.RemoteError(libvirt.libvirtError, 'val', 'traceback')) + self.assertRaises(rpc.RemoteError, + self.manager.live_migration, + self.ctxt, + 'i-12345', + 'host1') + + def test14(self): + """14: rpc.call returns 0 (cpu is not compatible between src and dest) """ + rpc.call = Mock(return_value = 0) + try : + self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + except exception.Invalid, e: + c1 = ( 0 <= e.message.find('doesnt have compatibility to')) + self.assertTrue(c1, True) + return False + + def test15(self): + """15: raise NotEmpty if host doesnt have enough resource. """ + + # Mocks for has_enough_resource() + db.instance_get = Mock(return_value = self.instance8) + + # Mocks for live_migration() + db.instance_get_by_internal_id = Mock(return_value = self.instance8) + db.instance_set_state = Mock(return_value = True) + rpc_cast = Mock(return_value = True) + + try : + self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + except exception.NotEmpty, e: + c1 = ( 0 <= e.message.find('doesnt have enough resource') ) + self.assertTrue(c1, True) + return False + + + def test16(self): + """16: everything goes well. """ + + db.instance_get_by_internal_id = Mock(return_value = self.instance8) + db.instance_set_state = Mock(return_value = True) + rpc.cast = Mock(return_value = True) + + ret= self.manager.live_migration(self.ctxt, 'i-12345', 'host1') + self.assertEqual(ret, None) + + + def tearDown(self): + """common terminating method. """ + #sys.stdout = self.stdoutBak + pass + +if __name__ == '__main__': + #unittest.main() + suite = unittest.TestLoader().loadTestsFromTestCase(SchedulerTestFunctions) + unittest.TextTestRunner(verbosity=3).run(suite) + + diff --git a/nova/livemigration_test/UT/testCase_UT.xls b/nova/livemigration_test/UT/testCase_UT.xls new file mode 100644 index 000000000..f73e8c5aa Binary files /dev/null and b/nova/livemigration_test/UT/testCase_UT.xls differ -- cgit From 85acbbe916df8b2d18f0dc3a0b8cad9fcfdd6907 Mon Sep 17 00:00:00 2001 From: masumotok Date: Mon, 27 Dec 2010 17:49:07 +0900 Subject: launch_at を前回コミット時に追加したが、lauched_atというカラムが既に存在し、 紛らわしいのでlauched_onにした。 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nova/compute/manager.py | 2 +- nova/db/sqlalchemy/models.py | 4 ++-- nova/scheduler/manager.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3e468b3a4..66bbb8d5a 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -105,7 +105,7 @@ class ComputeManager(manager.Manager): self.network_manager.setup_compute_network(context, instance_id) self.db.instance_update(context, instance_id, - {'host': self.host, 'launch_at':self.host}) + {'host': self.host, 'launched_on':self.host}) # TODO(vish) check to make sure the availability zone matches self.db.instance_set_state(context, diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 7f3a58bcb..c3e566c6a 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -234,9 +234,9 @@ class Instance(BASE, NovaBase): display_name = Column(String(255)) display_description = Column(String(255)) - # To remember at which host a instance booted. + # To remember on which host a instance booted. # An instance may moved to other host by live migraiton. - launch_at = Column(String(255)) + launched_on = Column(String(255)) # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 0921e3791..fe3ca9d5e 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -82,7 +82,7 @@ class SchedulerManager(manager.Manager): instance_id = instance_ref['id'] # 2. get src host and dst host - src = instance_ref['launch_at'] + src = instance_ref['launched_on'] shost_ref = db.host_get_by_name(context, src ) dhost_ref = db.host_get_by_name(context, dest) -- cgit From 8e1b74aa1c5a2f9113473eedc8e35b38b41445ea Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Mon, 27 Dec 2010 15:15:24 -0800 Subject: Added stack command-line tool --- nova/api/easy.py | 57 +++++++++++++++++++++++++++++++++------------ nova/compute/api.py | 6 +++++ nova/tests/easy_unittest.py | 6 ++--- nova/utils.py | 2 +- nova/wsgi.py | 3 +-- 5 files changed, 53 insertions(+), 21 deletions(-) (limited to 'nova') diff --git a/nova/api/easy.py b/nova/api/easy.py index 0e4f8a892..7468e3115 100644 --- a/nova/api/easy.py +++ b/nova/api/easy.py @@ -25,7 +25,7 @@ The general flow of a request is: (/controller/method) - Parameters are parsed from the request and passed to a method on the controller as keyword arguments. - - Optionally json_body is decoded to provide all the parameters. + - Optionally 'json' is decoded to provide all the parameters. - Actual work is done and a result is returned. - That result is turned into json and returned. @@ -94,7 +94,7 @@ class SundayMorning(wsgi.Router): def __init__(self, mapper=None): if mapper is None: mapper = routes.Mapper() - + self._load_registered_routes(mapper) super(SundayMorning, self).__init__(mapper=mapper) @@ -103,14 +103,18 @@ class SundayMorning(wsgi.Router): mapper.connect('/%s/{action}' % route, controller=ServiceWrapper(EASY_ROUTES[route])) - + class Reflection(object): + """Reflection methods to list available methods.""" def __init__(self): self._methods = {} + self._controllers = {} def _gather_methods(self): methods = {} + controllers = {} for route, handler in EASY_ROUTES.iteritems(): + controllers[route] = handler.__doc__.split('\n')[0] for k in dir(handler): if k.startswith('_'): continue @@ -120,40 +124,63 @@ class Reflection(object): # bunch of ugly formatting stuff argspec = inspect.getargspec(f) - args = [x for x in argspec[0] if x != 'self' and x != 'context'] + args = [x for x in argspec[0] + if x != 'self' and x != 'context'] defaults = argspec[3] and argspec[3] or [] args_r = list(reversed(args)) defaults_r = list(reversed(defaults)) + args_out = [] while args_r: if defaults_r: - args_out.append((args_r.pop(0), defaults_r.pop(0))) + args_out.append((args_r.pop(0), + repr(defaults_r.pop(0)))) else: - args_out.append(str(args_r.pop(0))) + args_out.append((str(args_r.pop(0)),)) + + # if the method accepts keywords + if argspec[2]: + args_out.insert(0, ('**%s' % argspec[2],)) methods['/%s/%s' % (route, k)] = { + 'short_doc': f.__doc__.split('\n')[0], + 'doc': f.__doc__, 'name': k, 'args': list(reversed(args_out))} - return methods + + self._methods = methods + self._controllers = controllers + + def get_controllers(self, context): + """List available controllers.""" + if not self._controllers: + self._gather_methods() + + return self._controllers def get_methods(self, context): + """List available methods.""" if not self._methods: - self._methods = self._gather_methods() + self._gather_methods() method_list = self._methods.keys() method_list.sort() - return {'methods': method_list} + methods = {} + for k in method_list: + methods[k] = self._methods[k]['short_doc'] + return methods def get_method_info(self, context, method): + """Get detailed information about a method.""" if not self._methods: - self._methods = self._gather_methods() + self._gather_methods() return self._methods[method] class ServiceWrapper(wsgi.Controller): def __init__(self, service_handle): self.service_handle = service_handle - + @webob.dec.wsgify def __call__(self, req): arg_dict = req.environ['wsgiorg.routing_args'][1] @@ -165,10 +192,10 @@ class ServiceWrapper(wsgi.Controller): params = {} if 'openstack.params' in req.environ: params = req.environ['openstack.params'] - + # TODO(termie): do some basic normalization on methods method = getattr(self.service_handle, action) - + result = method(context, **params) if type(result) is dict or type(result) is list: return self._serialize(result, req) @@ -181,7 +208,7 @@ class Proxy(object): def __init__(self, app, prefix=None): self.app = app self.prefix = prefix - + def __do_request(self, path, context, **kwargs): req = webob.Request.blank(path) req.method = 'POST' @@ -196,7 +223,7 @@ class Proxy(object): def __getattr__(self, key): if self.prefix is None: return self.__class__(self.app, prefix=key) - + def _wrapper(context, **kwargs): return self.__do_request('/%s/%s' % (self.prefix, key), context, diff --git a/nova/compute/api.py b/nova/compute/api.py index 5f18539a3..005ed7a68 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -40,6 +40,7 @@ def id_to_default_hostname(internal_id): """Default function to generate a hostname given an instance reference.""" return str(internal_id) + def id_to_ec2_hostname(internal_id): digits = [] while internal_id != 0: @@ -47,9 +48,11 @@ def id_to_ec2_hostname(internal_id): digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) return "i-%s" % ''.join(reversed(digits)) + HOSTNAME_FORMATTERS = {'default': id_to_default_hostname, 'ec2': id_to_ec2_hostname} + class ComputeAPI(base.Base): """API for interacting with the compute manager.""" @@ -63,6 +66,7 @@ class ComputeAPI(base.Base): super(ComputeAPI, self).__init__(**kwargs) def get_network_topic(self, context, instance_id): + """Get the network topic for an instance.""" try: instance = self.db.instance_get_by_internal_id(context, instance_id) @@ -221,6 +225,7 @@ class ComputeAPI(base.Base): return self.db.instance_update(context, instance_id, kwargs) def delete_instance(self, context, instance_id): + """Terminate and remove an instance.""" logging.debug("Going to try and terminate %d" % instance_id) try: instance = self.db.instance_get_by_internal_id(context, @@ -264,6 +269,7 @@ class ComputeAPI(base.Base): return self.db.instance_get_all(context) def get_instance(self, context, instance_id): + """Get information about a specific instance.""" rv = self.db.instance_get_by_internal_id(context, instance_id) return dict(rv.iteritems()) diff --git a/nova/tests/easy_unittest.py b/nova/tests/easy_unittest.py index 81990d842..cd13c7710 100644 --- a/nova/tests/easy_unittest.py +++ b/nova/tests/easy_unittest.py @@ -31,6 +31,7 @@ from nova.api import easy from nova.compute import api as compute_api from nova.tests import cloud_unittest + class FakeService(object): def echo(self, context, data): return {'data': data} @@ -49,7 +50,7 @@ class EasyTestCase(test.TestCase): easy.SundayMorning())) self.auth_router = easy.DelegatedAuthMiddleware(self.router) self.context = context.RequestContext('user1', 'proj1') - + def tearDown(self): easy.EASY_ROUTES = {} @@ -61,7 +62,7 @@ class EasyTestCase(test.TestCase): data = json.loads(resp.body) self.assertEqual(data['user'], 'user1') self.assertEqual(data['project'], 'proj1') - + def test_json_params(self): req = webob.Request.blank('/fake/echo') req.environ['openstack.context'] = self.context @@ -99,4 +100,3 @@ class EasyCloudTestCase(cloud_unittest.CloudTestCase): def tearDown(self): super(EasyCloudTestCase, self).tearDown() easy.EASY_ROUTES = {} - diff --git a/nova/utils.py b/nova/utils.py index 7a98ffa5a..337924f10 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -386,7 +386,7 @@ def dumps(value): return json.dumps(value) except TypeError: pass - + return json.dumps(to_primitive(value)) diff --git a/nova/wsgi.py b/nova/wsgi.py index c40f043f9..564805ae7 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -105,8 +105,7 @@ class Application(object): class Middleware(Application): """Base WSGI middleware. - - Modelled after Django's middleware this class allows you to + These classes require an application to be initialized that will be called next. By default the middleware will simply call its wrapped app, or you can override __call__ to customize its -- cgit From ea28b3117b02bcfd26e4017e850313cf5272d354 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Fri, 31 Dec 2010 12:43:40 +0900 Subject: deleting README.livemigration.txt and nova/livemigration_test/* --- nova/livemigration_test/SI/picture.pptx | Bin 137730 -> 0 bytes nova/livemigration_test/SI/testCase_SI.xls | Bin 49152 -> 0 bytes .../SI/testParameterSheet_SI.xls | Bin 464384 -> 0 bytes nova/livemigration_test/SI/utils/demo-firstboot.sh | 39 -- .../SI/utils/demo-runInstance.sh | 57 --- nova/livemigration_test/SI/utils/nova-manage.conf | 18 - nova/livemigration_test/SI/utils/nova.conf | 10 - nova/livemigration_test/SI/utils/nova.sh | 180 -------- nova/livemigration_test/SI/utils/nova.sh.compute | 37 -- nova/livemigration_test/UT/computeManager.test.py | 411 ------------------- .../UT/libvirtConnection.test.py | 382 ----------------- nova/livemigration_test/UT/nova-manage.test.py | 313 -------------- .../livemigration_test/UT/schedulerManager.test.py | 456 --------------------- nova/livemigration_test/UT/testCase_UT.xls | Bin 203776 -> 0 bytes 14 files changed, 1903 deletions(-) delete mode 100644 nova/livemigration_test/SI/picture.pptx delete mode 100644 nova/livemigration_test/SI/testCase_SI.xls delete mode 100644 nova/livemigration_test/SI/testParameterSheet_SI.xls delete mode 100755 nova/livemigration_test/SI/utils/demo-firstboot.sh delete mode 100755 nova/livemigration_test/SI/utils/demo-runInstance.sh delete mode 100644 nova/livemigration_test/SI/utils/nova-manage.conf delete mode 100644 nova/livemigration_test/SI/utils/nova.conf delete mode 100755 nova/livemigration_test/SI/utils/nova.sh delete mode 100755 nova/livemigration_test/SI/utils/nova.sh.compute delete mode 100644 nova/livemigration_test/UT/computeManager.test.py delete mode 100644 nova/livemigration_test/UT/libvirtConnection.test.py delete mode 100644 nova/livemigration_test/UT/nova-manage.test.py delete mode 100644 nova/livemigration_test/UT/schedulerManager.test.py delete mode 100644 nova/livemigration_test/UT/testCase_UT.xls (limited to 'nova') diff --git a/nova/livemigration_test/SI/picture.pptx b/nova/livemigration_test/SI/picture.pptx deleted file mode 100644 index b47bec9b5..000000000 Binary files a/nova/livemigration_test/SI/picture.pptx and /dev/null differ diff --git a/nova/livemigration_test/SI/testCase_SI.xls b/nova/livemigration_test/SI/testCase_SI.xls deleted file mode 100644 index be98b391a..000000000 Binary files a/nova/livemigration_test/SI/testCase_SI.xls and /dev/null differ diff --git a/nova/livemigration_test/SI/testParameterSheet_SI.xls b/nova/livemigration_test/SI/testParameterSheet_SI.xls deleted file mode 100644 index 400b43b43..000000000 Binary files a/nova/livemigration_test/SI/testParameterSheet_SI.xls and /dev/null differ diff --git a/nova/livemigration_test/SI/utils/demo-firstboot.sh b/nova/livemigration_test/SI/utils/demo-firstboot.sh deleted file mode 100755 index 3a6f7fb0b..000000000 --- a/nova/livemigration_test/SI/utils/demo-firstboot.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -DIR=/opt/nova-2010.1 - -# 1. 管理者ユーザを作成する -# nova-manage user admin ユーザ名 access-key secret-key -# -#$DIR/bin/nova-manage user admin admin admin admin - -# 2. プロジェクトを作成する -# nova-manage create project プロジェクト名 プロジェクトに属するユーザ名 -# -#$DIR/bin/nova-manage project create admin admin - -# 3. クラウドを使うための認証情報を生成する -# nova-manage project environment プロジェクト名 ユーザ名 認証情報を格納するファイル -# -#$DIR/bin/nova-manage project environment admin admin $DIR/novarc - -# 4. 認証情報の読み込み -. $DIR/novarc - -# 5. プロジェクト用仮想マシンネットワークの作成を行う -# nova-manage user admin ユーザ名 access-key secret-key -# -$DIR/bin/nova-manage network create 10.0.0.0/8 3 16 - -# 6. 初回ログインにはSSHの公開鍵認証が必要 -# -if [ "" == "`euca-describe-keypairs | grep testkey`" ]; then - euca-add-keypair testkey > testkey.pem -fi - -# 7. -for i in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do - sudo ip addr del $i dev eth0 2> /dev/null -done - - diff --git a/nova/livemigration_test/SI/utils/demo-runInstance.sh b/nova/livemigration_test/SI/utils/demo-runInstance.sh deleted file mode 100755 index 171291262..000000000 --- a/nova/livemigration_test/SI/utils/demo-runInstance.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -DIR=/opt/nova-2010.1 - -function inc_assigned(){ - assigned=`expr $assigned + 1` -} - - -# 1. 認証情報の読み込み -. $DIR/novarc - -# 3. 仮想マシンの起動 -# -ret=`euca-run-instances -t m1.small -k testkey ami-centos` -#ret=`euca-run-instances -t m1.small -k testkey ami-tiny` - -# 4. 仮想マシン用IPの確保 -# 未登録なら登録しておく -registered=`euca-describe-addresses` -for ip in 172.19.0.134 172.19.0.135 172.19.0.136 172.19.0.137 ; do - - not_registered=`echo $registered | grep $ip` - if [ "" == "$not_registered" ]; then - echo "[INFO] registed $ip" - $DIR/bin/nova-manage floating create `hostname` $ip - fi -done - -# 5. IPの割当 -echo 0 > /tmp/demo-runinstance -euca-describe-addresses | grep -v reserved | while read line; do - # 割り当てられてないものを仮想マシンに割り当てる - ip=`echo $line | cut -d ' ' -f 2` - id=`echo $ret | cut -d ' ' -f 5` - if [ "" == "`echo $id | grep i- `" ] ; then - echo "[INFO] try again" $ret - break - fi - echo "[INFO] assigned to ipaddr($ip) to instance($id) " - euca-associate-address -i $id $ip - echo 1 > /tmp/demo-runinstance - break -done - -echo $assigned -if [ 0 -eq "`cat /tmp/demo-runinstance`" ] ; then - echo "[INFO] address is full." -fi -rm -rf /tmp/demo-runinstance - - -# 6. FWの設定 -euca-authorize -P tcp -p 22 default 2> /dev/null > /dev/null -euca-authorize -P tcp -p 80 default 2> /dev/null > /dev/null -euca-authorize -P tcp -p 5555 default 2> /dev/null > /dev/null - diff --git a/nova/livemigration_test/SI/utils/nova-manage.conf b/nova/livemigration_test/SI/utils/nova-manage.conf deleted file mode 100644 index 9f8a02b96..000000000 --- a/nova/livemigration_test/SI/utils/nova-manage.conf +++ /dev/null @@ -1,18 +0,0 @@ ---verbose ---nodaemon ---dhcpbridge_flagfile=/etc/nova/nova-manage.conf ---FAKE_subdomain=ec2 ---libvirt_type=qemu ---no_internet_conn=True ---public_netif=eth0 ---public_interface=eth0 - ---cc-host=172.19.0.131 ---routing_source_ip=172.19.0.131 ---sql_connection=mysql://root:nova@172.19.0.131/nova ---rabbit_host=172.19.0.131 ---redis_host=172.19.0.131 ---s3_host=172.19.0.131 ---auth_driver=nova.auth.ldapdriver.LdapDriver ---ldap_url=ldap://172.19.0.131 - diff --git a/nova/livemigration_test/SI/utils/nova.conf b/nova/livemigration_test/SI/utils/nova.conf deleted file mode 100644 index c66bfbc53..000000000 --- a/nova/livemigration_test/SI/utils/nova.conf +++ /dev/null @@ -1,10 +0,0 @@ ---verbose ---nodaemon ---dhcpbridge_flagfile=/opt/nova-2010.4//bin/nova.conf ---network_manager=nova.network.manager.VlanManager ---cc_host=172.19.0.131 ---routing_source_ip=172.19.0.131 ---sql_connection=mysql://root:nova@localhost/nova ---auth_driver=nova.auth.ldapdriver.LdapDriver ---libvirt_type=qemu ---public_interface=eth0 diff --git a/nova/livemigration_test/SI/utils/nova.sh b/nova/livemigration_test/SI/utils/nova.sh deleted file mode 100755 index b8e2e9f26..000000000 --- a/nova/livemigration_test/SI/utils/nova.sh +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env bash -DIR=`pwd` -CMD=$1 -SOURCE_BRANCH=lp:nova -if [ -n "$2" ]; then - SOURCE_BRANCH=$2 -fi -#DIRNAME=nova -DIRNAME="" -NOVA_DIR=$DIR/$DIRNAME -if [ -n "$3" ]; then - NOVA_DIR=$DIR/$3 -fi - -if [ ! -n "$HOST_IP" ]; then - # NOTE(vish): This will just get the first ip in the list, so if you - # have more than one eth device set up, this will fail, and - # you should explicitly set HOST_IP in your environment - HOST_IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` -fi - -#USE_MYSQL=${USE_MYSQL:-0} -USE_MYSQL=1 -MYSQL_PASS=${MYSQL_PASS:-nova} -TEST=${TEST:-0} -#USE_LDAP=${USE_LDAP:-0} -USE_LDAP=1 -LIBVIRT_TYPE=${LIBVIRT_TYPE:-qemu} -NET_MAN=${NET_MAN:-VlanManager} -# NOTE(vish): If you are using FlatDHCP on multiple hosts, set the interface -# below but make sure that the interface doesn't already have an -# ip or you risk breaking things. -# FLAT_INTERFACE=eth0 - -if [ "$USE_MYSQL" == 1 ]; then - SQL_CONN=mysql://root:$MYSQL_PASS@localhost/nova -else - SQL_CONN=sqlite:///$NOVA_DIR/nova.sqlite -fi - -if [ "$USE_LDAP" == 1 ]; then - AUTH=ldapdriver.LdapDriver -else - AUTH=dbdriver.DbDriver -fi - -mkdir -p /etc/nova -cat >$NOVA_DIR/bin/nova.conf << NOVA_CONF_EOF ---verbose ---nodaemon ---dhcpbridge_flagfile=$NOVA_DIR/bin/nova.conf ---network_manager=nova.network.manager.$NET_MAN ---cc_host=$HOST_IP ---routing_source_ip=$HOST_IP ---sql_connection=$SQL_CONN ---auth_driver=nova.auth.$AUTH ---libvirt_type=$LIBVIRT_TYPE ---public_interface=eth0 -NOVA_CONF_EOF - -if [ -n "$FLAT_INTERFACE" ]; then - echo "--flat_interface=$FLAT_INTERFACE" >>$NOVA_DIR/bin/nova.conf -fi - -if [ "$CMD" == "branch" ]; then - sudo apt-get install -y bzr - rm -rf $NOVA_DIR - bzr branch $SOURCE_BRANCH $NOVA_DIR - cd $NOVA_DIR - mkdir -p $NOVA_DIR/instances - mkdir -p $NOVA_DIR/networks -fi - -# You should only have to run this once -if [ "$CMD" == "install" ]; then - sudo apt-get install -y python-software-properties - sudo add-apt-repository ppa:nova-core/ppa - sudo apt-get update - sudo apt-get install -y dnsmasq kpartx kvm gawk iptables ebtables - sudo apt-get install -y user-mode-linux kvm libvirt-bin - sudo apt-get install -y screen euca2ools vlan curl rabbitmq-server - sudo apt-get install -y lvm2 iscsitarget open-iscsi - echo "ISCSITARGET_ENABLE=true" | sudo tee /etc/default/iscsitarget - sudo /etc/init.d/iscsitarget restart - sudo modprobe kvm - sudo /etc/init.d/libvirt-bin restart - sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot - sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy - sudo apt-get install -y python-libvirt python-libxml2 python-routes - if [ "$USE_MYSQL" == 1 ]; then - cat </etc/nova/nova-manage.conf << NOVA_CONF_EOF ---verbose ---nodaemon ---dhcpbridge_flagfile=/etc/nova/nova-manage.conf ---FAKE_subdomain=ec2 ---libvirt_type=qemu ---no_internet_conn=True ---public_netif=eth0 ---public_interface=eth0 - ---cc-host=$HOST_IP ---routing_source_ip=$HOST_IP ---sql_connection=mysql://root:nova@$HOST_IP/nova ---rabbit_host=$HOST_IP ---redis_host=$HOST_IP ---s3_host=$HOST_IP ---auth_driver=nova.auth.ldapdriver.LdapDriver ---ldap_url=ldap://$HOST_IP - -NOVA_CONF_EOF - -$DIR/bin/nova-compute --flagfile=/etc/nova/nova-manage.conf - diff --git a/nova/livemigration_test/UT/computeManager.test.py b/nova/livemigration_test/UT/computeManager.test.py deleted file mode 100644 index 69ee876d1..000000000 --- a/nova/livemigration_test/UT/computeManager.test.py +++ /dev/null @@ -1,411 +0,0 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - - -import sys -import os -import unittest -import commands -import re -import logging - -from mock import Mock -import twisted - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - -try: - print - print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' \ - % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.compute.manager import ComputeManager - from nova.virt.libvirt_conn import LibvirtConnection - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - - def write(self, arg): - self.buffer += arg - - def writelines(self, arg): - self.buffer += arg - - def flush(self): - print 'flush' - self.buffer = '' - - -class tmpStderr(tmpStdout): - def write(self, arg): - self.buffer += arg - - def flush(self): - pass - - def realFlush(self): - self.buffer = '' - -dummyCallReturnValue={ 0:True } -dummyCallCount=0 -def dummyCall(context, topic, method): - global dummyCallReturnValue, dummyCallCount - if dummyCallCount in dummyCallReturnValue.keys() : - ret = dummyCallReturnValue[ dummyCallCount ] - dummyCallCount += 1 - return ret - else : - dummyCallCount += 1 - return False - - -class ComputeTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - manager = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - #if self.stdout is None: - # self.__class__.stdout = tmpStdout() - #self.stdoutBak = sys.stdout - #sys.stdout = self.stdout - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - self.host = 'openstack2-api' - if self.manager is None: - self.__class__.manager = ComputeManager(host=self.host) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - for key, val in [('name', 'host1'), ('cpu', 5), - ('memory_mb', 20480), ('hdd_gb', 876)]: - self.host1.__setitem__(key, val) - - self.host2 = Host() - for key, val in [('name', 'host2'), ('cpu', 5), - ('memory_mb', 20480), ('hdd_gb', 876)]: - self.host2.__setitem__(key, val) - - self.instance1 = Instance() - for key, val in [('id', 1), ('host', 'host1'), - ('hostname', 'i-12345'), ('state', power_state.RUNNING), - ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), - ('hdd_gb', 5), ('internal_id', 12345)]: - self.instance1.__setitem__(key, val) - - self.instance2 = Instance() - for key, val in [('id', 2), ('host', 'host1'), - ('hostname', 'i-12345'), ('state', power_state.RUNNING), - ('project_id', 'testPJ'), ('vcpus', 3), ('memory_mb', 1024), - ('hdd_gb', 5)]: - self.instance2.__setitem__(key, val) - - self.fixed_ip1 = FixedIp() - for key, val in [('id', 1), ('address', '1.1.1.1'), - ('network_id', '1'), ('instance_id', 1)]: - self.fixed_ip1.__setitem__(key, val) - - self.vol1 = Volume() - for key, val in [('id', 1), ('ec2_id', 'vol-qijjuc7e'), - ('availability_zone', 'nova'), ('host', 'host1')]: - self.vol1.__setitem__(key, val) - - self.vol2 = Volume() - for key, val in [('id', 2), ('ec2_id', 'vol-qi22222'), - ('availability_zone', 'nova'), ('host', 'host1')]: - self.vol2.__setitem__(key, val) - - self.secgrp1 = Volume() - for key, val in [('id', 1), ('ec2_id', 'default')]: - self.secgrp1.__setitem__(key, val) - - self.secgrp2 = Volume() - for key, val in [('id', 2), ('ec2_id', 'def2')]: - self.secgrp2.__setitem__(key, val) - - self.netref1 = Network() - - def setMocks(self): - - # mocks for pre_live_migration - self.ctxt = context.get_admin_context() - db.instance_get = Mock(return_value=self.instance1) - db.volume_get_by_ec2_id = Mock(return_value=[self.vol1, self.vol2]) - db.volume_get_shelf_and_blade = Mock(return_value=(3, 4)) - db.instance_get_fixed_address = Mock(return_value=self.fixed_ip1) - db.security_group_get_by_instance \ - = Mock(return_value=[self.secgrp1, self.secgrp2]) - self.manager.driver.setup_nwfilters_for_instance \ - = Mock(return_value=None) - self.manager.driver.nwfilter_for_instance_exists = Mock(return_value=None) - self.manager.network_manager.setup_compute_network \ - = Mock(return_value=None) - # mocks for live_migration_ - rpc.call = Mock(return_value=True) - db.instance_set_state = Mock(return_value=True) - - # ---> test for nova.compute.manager.pre_live_migration() - def test01(self): - """01: NotFound error occurs on finding instance on DB. """ - - db.instance_get = Mock(side_effect=exception.NotFound('ERR')) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test02(self): - """02: NotAuthrized occurs on finding volume on DB. """ - - db.volume_get_by_ec2_id \ - = Mock(side_effect=exception.NotAuthorized('ERR')) - - self.assertRaises(exception.NotAuthorized, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test03(self): - """03: Unexpected exception occurs on finding volume on DB. """ - - db.volume_get_by_ec2_id = Mock(side_effect=TypeError('ERR')) - - self.assertRaises(TypeError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test04(self): - """04: no volume and fixed ip found on DB, """ - - db.volume_get_by_ec2_id = Mock(side_effect=exception.NotFound('ERR')) - db.instance_get_fixed_address = Mock(return_value=None) - - self.assertRaises(rpc.RemoteError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - c1 = (0 <= sys.stderr.buffer.find('has no volume')) - - self.assertEqual(c1, True) - - def test05(self): - """05: volume found and no fixed_ip found on DB. """ - - db.instance_get_fixed_address \ - = Mock(side_effect=exception.NotFound('ERR')) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test06(self): - """06: self.driver.setup_nwfilters_for_instance causes NotFound. """ - self.manager.driver.setup_nwfilters_for_instance \ - = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test07(self): - """07: self.network_manager.setup_compute_network causes ProcessExecutionError. """ - self.manager.network_manager.setup_compute_network \ - = Mock(side_effect=exception.ProcessExecutionError("ERR")) - - self.assertRaises(exception.ProcessExecutionError, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - - def test08(self): - """08: self.manager.network_manager.setup_compute_network - exception.NotFound. """ - self.manager.network_manager.setup_compute_network \ - = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.pre_live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - # those 2 cases are omitted : - # self.driver.setup_nwfilters_for_instance causes - # twisted.python.failure.Failure. - # self.driver.refresh_security_group causes twisted.python.failure.Failure. - # - # twisted.python.failure.Failure can not be used with assertRaises, - # it doesnt have __call___ - # - - def test09(self): - """09: volume/fixed_ip found on DB, all procedure finish - successfully.. """ - - result = self.manager.pre_live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - self.assertEqual(result, True) - - # ---> test for nova.compute.manager.live_migration() - - def test10(self): - """10: rpc.call(pre_live_migration returns Error(Not None). """ - rpc.call = Mock(side_effect=exception.NotFound("ERR")) - - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test11(self): - """11: if rpc.call returns rpc.RemoteError. """ - - rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) - db.instance_set_state = Mock(return_value=True) - result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - c1 = (None == result) - c2 = (0 <= sys.stderr.buffer.find('err at')) - self.assertEqual(c1 and c2, True) - - def test12(self): - """12: if rpc.call returns rpc.RemoteError and instance_set_state - also ends up err. (then , unexpected err occurs, in this case - TypeError) - """ - rpc.call = Mock(return_value=rpc.RemoteError(None, None, None)) - db.instance_set_state = Mock(side_effect=TypeError("ERR")) - self.assertRaises(TypeError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test13(self): - """13: if wait for pre_live_migration, but timeout. """ - rpc.call = dummyCall - - db.instance_get = Mock(return_value=self.instance1) - - result = self.manager.live_migration(self.ctxt, 'dummy_ec2_id', - 'host2') - c1 = (None == result) - c2 = (0 <= sys.stderr.buffer.find('Timeout for')) - self.assertEqual(c1 and c2, True) - - def test14(self): - """14: if db_instance_get issues NotFound. - """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(side_effect=exception.NotFound("ERR")) - self.assertRaises(exception.NotFound, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test15(self): - """15: if rpc.call returns True, and instance_get() cause other - exception. (Unexpected case - b/c it already checked by - nova-manage) - """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(side_effect=TypeError("ERR")) - - self.assertRaises(TypeError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test16(self): - """16: if rpc.call returns True, and live_migration issues - ProcessExecutionError. """ - rpc.call = Mock(return_value=True) - db.instance_get = Mock(return_value=self.instance1) - ret = self.manager.driver.live_migration \ - = Mock(side_effect=utils.ProcessExecutionError("ERR")) - - self.assertRaises(utils.ProcessExecutionError, - self.manager.live_migration, - self.ctxt, - 'dummy_ec2_id', - 'host2') - - def test17(self): - """17: everything goes well. """ - self.manager.driver.live_migration = Mock(return_value=True) - ret = self.manager.live_migration(self.ctxt, 'i-12345', 'host1') - self.assertEqual(True, True) - - def tearDown(self): - """common terminating method. """ - self.stderr.realFlush() - sys.stderr = self.stderrBak - #sys.stdout = self.stdoutBak - -if __name__ == '__main__': - logging.getLogger().setLevel(logging.DEBUG) - #unittest.main() - - suite = unittest.TestLoader().loadTestsFromTestCase(ComputeTestFunctions) - unittest.TextTestRunner(verbosity=2).run(suite) - - #suite = unittest.TestSuite() - #suite.addTest(ComputeTestFunctions("test15")) - #suite.addTest(ComputeTestFunctions("test16")) - #unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/nova/livemigration_test/UT/libvirtConnection.test.py b/nova/livemigration_test/UT/libvirtConnection.test.py deleted file mode 100644 index 0b737e140..000000000 --- a/nova/livemigration_test/UT/libvirtConnection.test.py +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - - -import sys -import os -import unittest -import commands -import re -import logging -import libvirt - -from mock import Mock -import twisted - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - - -try : - print - print 'checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.compute.manager import ComputeManager - from nova.virt import libvirt_conn - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def writelines(self, arg): - self.buffer += arg - def flush(self): - print 'flush' - self.buffer = '' - -class tmpStderr(tmpStdout): - def write(self,arg): - self.buffer += arg - def flush(self): - pass - def realFlush(self): - self.buffer = '' - -class DummyLibvirtConn(object): - nwfilterLookupByName = None - def __init__(self): - pass - - -class LibvirtConnectionTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - manager = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - #if self.stdout is None: - # self.__class__.stdout = tmpStdout() - #self.stdoutBak = sys.stdout - #sys.stdout = self.stdout - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - self.host = 'openstack2-api' - if self.manager is None: - self.__class__.manager = libvirt_conn.get_connection(False) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - for key, val in [ ('name', 'host1'), ('cpu', 5), ('memory_mb', 20480), ('hdd_gb', 876) ]: - self.host1.__setitem__(key, val) - - self.instance1 = Instance() - for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5), ('internal_id',12345) ]: - self.instance1.__setitem__(key, val) - - - self.instance2 = Instance() - for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('hdd_gb', 5) ]: - self.instance2.__setitem__(key, val) - - - self.fixed_ip1 = FixedIp() - for key, val in [ ('id', 1), ('address', '1.1.1.1'), ('network_id', '1'), - ('instance_id', 1)]: - self.fixed_ip1.__setitem__(key, val) - - self.floating_ip1 = FloatingIp() - for key, val in [ ('id', 1), ('address', '1.1.1.200') ]: - self.floating_ip1.__setitem__(key, val) - - self.netref1 = Network() - for key, val in [ ('id', 1) ]: - self.netref1.__setitem__(key, val) - - - def setMocks(self): - - self.ctxt = context.get_admin_context() - db.instance_get_fixed_address = Mock(return_value = '1.1.1.1') - db.fixed_ip_update = Mock(return_value = None) - db.fixed_ip_get_network = Mock(return_value = self.netref1) - db.network_update = Mock(return_value = None) - db.instance_get_floating_address = Mock(return_value = '1.1.1.200') - db.floating_ip_get_by_address = Mock(return_value = self.floating_ip1) - db.floating_ip_update = Mock(return_value = None) - db.instance_update = Mock(return_value = None) - - - # ---> test for nova.virt.libvirt_conn.nwfilter_for_instance_exists() - - def test01(self): - """01: libvirt.libvirtError occurs. """ - - self.manager._wrapped_conn = DummyLibvirtConn() - self.manager._test_connection = Mock(return_value=True) - self.manager._conn.nwfilterLookupByName = \ - Mock(side_effect=libvirt.libvirtError("ERR")) - ret = self.manager.nwfilter_for_instance_exists(self.instance1) - self.assertEqual(ret, False) - - def test02(self): - """02: libvirt.libvirtError not occurs. """ - - self.manager._wrapped_conn = DummyLibvirtConn() - self.manager._test_connection = Mock(return_value=True) - self.manager._conn.nwfilterLookupByName = \ - Mock(return_value=True) - ret = self.manager.nwfilter_for_instance_exists(self.instance1) - self.assertEqual(ret, True) - - # ---> test for nova.virt.libvirt_conn.live_migraiton() - - def test03(self): - """03: Unexpected exception occurs on finding volume on DB. """ - - utils.execute = Mock( side_effect=exception.ProcessExecutionError('ERR') ) - - self.assertRaises(exception.ProcessExecutionError, - self.manager._live_migration, - self.ctxt, - self.instance1, - 'host2') - - # ---> other case cannot be tested because live_migraiton - # is synchronized/asynchronized method are mixed together - - - # ---> test for nova.virt.libvirt_conn._post_live_migraiton - - def test04(self): - """04: instance_ref is not nova.db.sqlalchemy.models.Instances""" - - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.ctxt, - "dummy string", - 'host2') - - def test05(self): - """05: db.instance_get_fixed_address return None""" - - db.instance_get_fixed_address = Mock( return_value=None ) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('fixed_ip is not found')) - self.assertEqual(c1 and c2, True) - - def test06(self): - """06: db.instance_get_fixed_address raises NotFound""" - - db.instance_get_fixed_address = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host2') - - def test07(self): - """07: db.instance_get_fixed_address raises Unknown exception""" - - db.instance_get_fixed_address = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - def test08(self): - """08: db.fixed_ip_update return NotFound. """ - - db.fixed_ip_update = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - def test09(self): - """09: db.fixed_ip_update return NotAuthorized. """ - db.fixed_ip_update = Mock( side_effect=exception.NotAuthorized('ERR') ) - self.assertRaises(exception.NotAuthorized, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - def test10(self): - """10: db.fixed_ip_update return Unknown exception. """ - db.fixed_ip_update = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - def test11(self): - """11: db.fixed_ip_get_network causes NotFound. """ - - db.fixed_ip_get_network = Mock( side_effect=exception.NotFound('ERR') ) - self.assertRaises(exception.NotFound, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - # not tested db.fixed_ip_get_network raises NotAuthorized - # because same test has been done at previous test. - - def test12(self): - """12: db.fixed_ip_get_network causes Unknown exception. """ - - db.fixed_ip_get_network = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - def test13(self): - """13: db.network_update raises Unknown exception. """ - db.network_update = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - def test14(self): - """14: db.instance_get_floating_address raises NotFound. """ - db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - - def test15(self): - """15: db.instance_get_floating_address returns None. """ - - db.instance_get_floating_address = Mock( return_value=None ) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('floating_ip is not found')) - self.assertEqual(c1 and c2, True) - - def test16(self): - """16: db.instance_get_floating_address raises NotFound. """ - - db.instance_get_floating_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - def test17(self): - """17: db.instance_get_floating_address raises Unknown exception. """ - db.instance_get_floating_address = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - - def test18(self): - """18: db.floating_ip_get_by_address raises NotFound """ - - db.floating_ip_get_by_address = Mock(side_effect=exception.NotFound("ERR")) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('doesnt have floating_ip')) - self.assertEqual(c1 and c2, True) - - def test19(self): - """19: db.floating_ip_get_by_address raises Unknown exception. """ - db.floating_ip_get_by_address = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - - def test20(self): - """20: db.floating_ip_update raises Unknown exception. - """ - db.floating_ip_update = Mock(side_effect=TypeError("ERR")) - ret = self.manager._post_live_migration(self.ctxt, self.instance1, 'host1') - c1 = (ret == None) - c2 = (0 <= sys.stderr.buffer.find('Live migration: Unexpected error')) - self.assertEqual(c1 and c2, True) - - def test21(self): - """21: db.instance_update raises unknown exception. """ - - db.instance_update = Mock(side_effect=TypeError("ERR")) - self.assertRaises(TypeError, - self.manager._post_live_migration, - self.ctxt, - self.instance1, - 'host1') - - def tearDown(self): - """common terminating method. """ - self.stderr.realFlush() - sys.stderr = self.stderrBak - #sys.stdout = self.stdoutBak - -if __name__ == '__main__': - logging.getLogger().setLevel(logging.DEBUG) - #unittest.main() - - suite = unittest.TestLoader().loadTestsFromTestCase(LibvirtConnectionTestFunctions) - unittest.TextTestRunner(verbosity=2).run(suite) - - #suite = unittest.TestSuite() - #suite.addTest(LibvirtConnectionTestFunctions("test14")) - #suite.addTest(LibvirtConnectionTestFunctions("test16")) - #unittest.TextTestRunner(verbosity=2).run(suite) - - diff --git a/nova/livemigration_test/UT/nova-manage.test.py b/nova/livemigration_test/UT/nova-manage.test.py deleted file mode 100644 index 6db15cea0..000000000 --- a/nova/livemigration_test/UT/nova-manage.test.py +++ /dev/null @@ -1,313 +0,0 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -NOVA_DIR='/opt/nova-2010.4' - -import sys -import os -import unittest -import commands -import re - -from mock import Mock - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - - -try : - print - print 'Testing %s/bin/nova-manage, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def flush(self): - self.buffer = '' - -class tmpStderr(tmpStdout): - def write(self, arg): - self.buffer += arg - def flush(self): - pass - def realFlush(self): - self.buffer = '' - - -class NovaManageTestFunctions(unittest.TestCase): - - stdout = None - stdoutBak = None - stderr = None - stderrBak = None - - hostCmds = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - commands.getstatusoutput('cp -f %s/bin/nova-manage %s' % ( NOVA_DIR, self.getNovaManageCopyPath() )) - commands.getstatusoutput('touch %s' % self.getInitpyPath() ) - try : - import bin.novamanagetest - except: - print 'Fail to import nova-manage . check bin/nova-manage exists' - raise - - # replace stdout for checking nova-manage output - if self.stdout is None : - self.__class__.stdout = tmpStdout() - self.stdoutBak = sys.stdout - sys.stdout = self.stdout - - # replace stderr for checking nova-manage output - if self.stderr is None: - self.__class__.stderr = tmpStderr() - self.stderrBak = sys.stderr - sys.stderr = self.stderr - - # prepare test data - self.setTestData() - - - def setTestData(self): - import bin.novamanagetest - - if self.hostCmds is None : - self.__class__.hostCmds = bin.novamanagetest.HostCommands() - self.instanceCmds = bin.novamanagetest.InstanceCommands() - - self.host1 = Host() - self.host1.__setitem__('name', 'host1') - - self.host2 = Host() - self.host2.__setitem__('name', 'host2') - - self.instance1 = Instance() - self.instance1.__setitem__('id', 1) - self.instance1.__setitem__('host', 'host1') - self.instance1.__setitem__('hostname', 'i-12345') - self.instance1.__setitem__('state', power_state.NOSTATE) - self.instance1.__setitem__('state_description', 'running') - - self.instance2 = Instance() - self.instance2.__setitem__('id', 2) - self.instance2.__setitem__('host', 'host1') - self.instance2.__setitem__('hostname', 'i-12345') - self.instance2.__setitem__('state', power_state.RUNNING) - self.instance2.__setitem__('state_description', 'pending') - - self.instance3 = Instance() - self.instance3.__setitem__('id', 3) - self.instance3.__setitem__('host', 'host1') - self.instance3.__setitem__('hostname', 'i-12345') - self.instance3.__setitem__('state', power_state.RUNNING) - self.instance3.__setitem__('state_description', 'running') - - db.host_get_all = Mock(return_value=[self.host1, self.host2]) - - def getInitpyPath(self): - return '%s/bin/__init__.py' % NOVA_DIR - - def getNovaManageCopyPath(self): - return '%s/bin/novamanagetest.py' % NOVA_DIR - - # -----> Test for nova-manage host list - - def test01(self): - """01: Got some host lists. """ - - self.hostCmds.list() - - c1 = (2 == self.stdout.buffer.count('\n')) - c2 = (0 <= self.stdout.buffer.find('host1')) - c3 = (0 <= self.stdout.buffer.find('host2')) - self.assertEqual(c1 and c2 and c3, True) - - def test02(self): - """02: Got empty lsit. """ - - db.host_get_all = Mock(return_value=[]) - self.hostCmds.list() - - # result should be empty - c = (0 == len(self.stdout.buffer) ) - self.assertEqual(c, True) - - def test03(self): - """03: Got notFound """ - - db.host_get_all = Mock(side_effect=exception.NotFound("ERR")) - self.assertRaises(exception.NotFound, self.hostCmds.list) - - # --------> Test For nova-manage host show - - def test04(self): - """04: args are not enough(nova-manage host show) """ - self.assertRaises(TypeError, self.hostCmds.show ) - - - def test05(self): - """05: nova-manage host show not-registered-host, and got an error""" - - rpc.call = Mock(return_value={'ret' : False, 'msg': 'ERR'} ) - self.hostCmds.show('host1') - self.assertEqual( self.stdout.buffer[:3]=='ERR', True ) - - - def test06(self): - """06: nova-manage host show registerd-host, and no project uses the host""" - - dic = {'ret': True, - 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, - 'usage': {}} - - rpc.call = Mock(return_value=dic ) - self.hostCmds.show('host1') - - # result should be : - # HOST PROJECT cpu mem(mb) disk(gb) - # host1 1 2 3 - line = self.stdout.buffer.split('\n')[1] - line = re.compile('\t+').sub(' ', line).strip() - c1 = ( 'host1 1 2 3' == line ) - c2 = ( self.stdout.buffer.count('\n') == 2 ) - - self.assertEqual( c1 and c2, True ) - - def test07(self): - """07: nova-manage host show registerd-host, - and some projects use the host - """ - dic = {'ret': True, - 'phy_resource': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, - 'usage': {'p1': {'vcpus':1, 'memory_mb':2, 'local_gb':3}, - 'p2': {'vcpus':1, 'memory_mb':2, 'local_gb':3} }} - - rpc.call = Mock(return_value=dic ) - self.hostCmds.show('host1') - - # result should be : - # HOST PROJECT cpu mem(mb) disk(gb) - # host1 1 2 3 - # host1 p1 1 2 3 - # host1 p2 4 5 6 - line = self.stdout.buffer.split('\n')[1] - ret = re.compile('\t+').sub(' ', line).strip() - c1 = ( 'host1 1 2 3' == ret ) - - line = self.stdout.buffer.split('\n')[2] - line = re.compile('\t+').sub(' ', line).strip() - c2 = ( 'host1 p1 1 2 3' == line ) or ( 'host1 p2 1 2 3' == line ) - - line = self.stdout.buffer.split('\n')[3] - ret = re.compile('\t+').sub(' ', line).strip() - c3 = ( 'host1 p1 1 2 3' == ret ) or ( 'host1 p2 1 2 3' == ret ) - - self.assertEqual( c1 and c2 and c3, True ) - - def test08(self): - """08: nova-manage host show registerd-host, and rpc.call returns None - (unexpected error) - """ - rpc.call = Mock(return_value=None ) - self.hostCmds.show('host1') - c1 = ( 0 <= self.stdout.buffer.find('Unexpected error') ) - self.assertEqual( c1, True ) - - # ----------> Test for bin/nova-manage instance live_migration - - def test09(self): - """09: arguments are not enough(nova-manage instances live_migration) - """ - self.assertRaises(TypeError, self.instanceCmds.live_migration ) - - def test10(self): - """10: arguments are not enough(nova-manage instances live_migration ec2_id) - """ - self.assertRaises(TypeError, self.instanceCmds.live_migration, 'i-xxx' ) - - def test11(self): - """11: nova-manage instances live_migration ec2_id(invalid id) host""" - - db.instance_get_by_internal_id = Mock( side_effect=exception.NotFound('ERR') ) - try : - self.instanceCmds.live_migration('i-xxx', 'host1') - except exception.NotFound, e: - c1 = (0 < str(e.args).find('is not found') ) - self.assertTrue(c1, True) - return False - - def test12(self): - """12: nova-manage instances live_migration ec2_id host - and db.instance_get_by_internal_id raises unexpected exceptioin. - """ - db.instance_get_by_internal_id = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, self.instanceCmds.live_migration, 'i-xxx' ) - - def test13(self): - """13: nova-manage instances live_migration ec2_id host, - rpc.call raises RemoteError because destination doesnt have enough resource. - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - rpc.call = Mock(return_value = rpc.RemoteError(TypeError, 'val', 'traceback')) - self.assertRaises(rpc.RemoteError, self.instanceCmds.live_migration, 'i-xxx', 'host2' ) - - - def test14(self): - """14: nova-manage instances live_migration ec2_id host, - everything goes well, ang gets success messages. - """ - db.host_get_by_name = Mock(return_value = self.host1) - db.instance_get_by_internal_id = Mock( return_value = self.instance3 ) - rpc.call = Mock(return_value = None) - - self.instanceCmds.live_migration('i-12345', 'host2') - c1 = (0 <= self.stdout.buffer.find('Finished all procedure') ) - self.assertEqual( c1, True ) - - - def tearDown(self): - """common terminating method. """ - commands.getstatusoutput('rm -rf %s' % self.getInitpyPath() ) - commands.getstatusoutput('rm -rf %s' % self.getNovaManageCopyPath() ) - sys.stdout.flush() - sys.stdout = self.stdoutBak - self.stderr.realFlush() - sys.stderr = self.stderrBak - -if __name__ == '__main__': - #unittest.main() - suite = unittest.TestLoader().loadTestsFromTestCase(NovaManageTestFunctions) - unittest.TextTestRunner(verbosity=3).run(suite) - - diff --git a/nova/livemigration_test/UT/schedulerManager.test.py b/nova/livemigration_test/UT/schedulerManager.test.py deleted file mode 100644 index 33a38c660..000000000 --- a/nova/livemigration_test/UT/schedulerManager.test.py +++ /dev/null @@ -1,456 +0,0 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - - -import sys -import os -import unittest -import commands -import re -import libvirt - -from mock import Mock - -# getting /nova-inst-dir -NOVA_DIR = os.path.abspath(sys.argv[0]) -for i in range(4): - NOVA_DIR = os.path.dirname(NOVA_DIR) - -try : - print - print 'Checking %s/bin/nova-manage exists, set the NOVA_DIR properly..' % NOVA_DIR - print - - sys.path.append(NOVA_DIR) - - from nova.scheduler.manager import SchedulerManager - - from nova import context - from nova import db - from nova import exception - from nova import flags - from nova import quota - from nova import utils - from nova.auth import manager - from nova.cloudpipe import pipelib - from nova import rpc - from nova.api.ec2 import cloud - from nova.compute import power_state - - from nova.db.sqlalchemy.models import * - -except: - print 'set correct NOVA_DIR in this script. ' - raise - - -class tmpStdout: - def __init__(self): - self.buffer = "" - def write(self,arg): - self.buffer += arg - def flush(self): - self.buffer = '' - - -class SchedulerTestFunctions(unittest.TestCase): - - manager = None - - # 共通の初期化処理 - def setUp(self): - """common init method. """ - - self.host = 'openstack2-api' - if self.manager is None: - self.manager = SchedulerManager(host=self.host) - - self.setTestData() - self.setMocks() - - def setTestData(self): - - self.host1 = Host() - self.host1.__setitem__('name', 'host1') - self.host1.__setitem__('vcpus', 5) - self.host1.__setitem__('memory_mb', 20480) - self.host1.__setitem__('local_gb', 876) - self.host1.__setitem__('cpu_info', 1) - - self.host2 = Host() - self.host2.__setitem__('name', 'host2') - self.host2.__setitem__('vcpus', 5) - self.host2.__setitem__('memory_mb', 20480) - self.host2.__setitem__('local_gb', 876) - self.host2.__setitem__('hypervisor_type', 'QEMU') - self.host2.__setitem__('hypervisor_version', 12003) - xml="x86_64NehalemIntel" - self.host2.__setitem__('cpu_info', xml) - - self.instance1 = Instance() - for key, val in [ ('id', 1), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance1.__setitem__(key, val) - - - self.instance2 = Instance() - for key, val in [ ('id', 2), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ'), - ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance2.__setitem__(key, val) - - - self.instance3 = Instance() - for key, val in [ ('id', 3), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5), - ('internal_id', 123456), ('state', 1), - ('state_description', 'running') ]: - self.instance3.__setitem__(key, val) - - self.instance4 = Instance() - for key, val in [ ('id', 4), ('host', 'host2'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5), - ('internal_id', 123456), ('state', 0), - ('state_description', 'running') ]: - self.instance4.__setitem__(key, val) - - self.instance5 = Instance() - for key, val in [ ('id', 5), ('host', 'host2'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 5), - ('internal_id', 123456), ('state', 1), - ('state_description', 'migrating') ]: - self.instance5.__setitem__(key, val) - - self.instance6 = Instance() - for key, val in [ ('id', 6), ('host', 'host2'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 3), ('memory_mb', 1024), ('local_gb', 5) ]: - self.instance6.__setitem__(key, val) - - self.instance7 = Instance() - for key, val in [ ('id', 7), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), ('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 18432), ('local_gb', 5) ]: - self.instance7.__setitem__(key, val) - - self.instance8 = Instance() - for key, val in [ ('id', 8), ('host', 'host1'), ('hostname', 'i-12345'), - ('state', power_state.RUNNING), - ('state_description', 'running'),('project_id', 'testPJ2'), - ('vcpus', 1), ('memory_mb', 1024), ('local_gb', 866) ]: - self.instance8.__setitem__(key, val) - - self.service1 = Service() - for key, val in [ ('id', 1), ('host', 'host1'), ('binary', 'nova-compute'), - ('topic', 'compute')]: - self.service1.__setitem__(key, val) - - self.service2 = Service() - for key, val in [ ('id', 2), ('host', 'host2'), ('binary', 'nova-compute'), - ('topic', 'compute')]: - self.service1.__setitem__(key, val) - - def setMocks(self): - self.ctxt = context.get_admin_context() - # Mocks for has_enough_resource() - db.instance_get = Mock(return_value = self.instance3) - db.host_get_by_name = Mock(return_value = self.host2) - db.instance_get_all_by_host = Mock(return_value = [self.instance4, self.instance5] ) - - # Mocks for live_migration - db.service_get_all_by_topic = Mock(return_value = [self.service1] ) - self.manager.service_ip_up = Mock(return_value = True) - rpc.call = Mock(return_value=1) - db.instance_set_state = Mock(return_value = True) - self.manager.driver.service_is_up = Mock(return_value = True) - - def check_format(self, val): - """check result format of show_host_resource """ - - if dict != type(val) : - sys.stderr.write('return value is not dict') - return False - - if not val.has_key('ret'): - sys.stderr.write('invalid format(missing "ret"). ') - return False - - if not val['ret'] : - if not val.has_key('msg') : - sys.stderr.write( 'invalid format(missing "msg").' ) - return False - - else : - if not val.has_key('phy_resource') : - sys.stderr.write('invalid format(missing "phy_resource"). ') - return False - - if not val.has_key('usage'): - sys.stderr.write('invalid format(missing "usage"). ') - return False - - if not self._check_format(val['phy_resource']): - return False - - for key, dic in val['usage'].items() : - if not self._check_format(dic): - return False - return True - - def _check_format(self, val): - if dict != type(val) : - sys.stderr.write('return value is not dict') - return False - - for key in ['vcpus', 'memory_mb', 'local_gb']: - if not val.has_key(key) : - sys.stderr.write('invalid format(missing "%s"). ' % key ) - return False - - return True - - - # ---> test for nova.scheduler.manager.show_host_resource() - - def test01(self): - """01: get NotFound exception when dest host not found on DB """ - - db.host_get_by_name = Mock( side_effect=exception.NotFound('ERR') ) - result = self.manager.show_host_resource(self.ctxt, 'not-registered-host') - c1 = ( not result['ret'] ) - c2 = ( 0 == result['msg'].find('No such') ) - self.assertEqual(c1 and c2, True) - - def test02(self): - """02: get other exception if unexpected err. """ - - db.host_get_by_name = Mock( side_effect=TypeError('ERR') ) - self.assertRaises(TypeError, self.manager.show_host_resource, self.ctxt, 'host1' ) - - def test03(self): - """03: no instance found on dest host. """ - - db.host_get_by_name = Mock( return_value = self.host1 ) - db.instance_get_all_by_host = Mock( return_value=[]) - ret= self.manager.show_host_resource(self.ctxt, 'host1') - - c1 = self.check_format(ret) - v = ret['phy_resource'] - c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) - c3 = ( 0 == len(ret['usage']) ) - - self.assertEqual(c1 and c2 and c3, True) - - def test04(self): - """04: some instance found on dest host. """ - - db.host_get_by_name = Mock( return_value = self.host1 ) - db.instance_get_all_by_host = Mock( return_value=[ self.instance1, - self.instance2, - self.instance3] ) - - db.instance_get_vcpu_sum_by_host_and_project = Mock(return_value=3) - db.instance_get_memory_sum_by_host_and_project = Mock(return_value=1024) - db.instance_get_disk_sum_by_host_and_project = Mock(return_value=5) - - ret= self.manager.show_host_resource(self.ctxt, 'host1') - - c1 = self.check_format(ret) - v = ret['phy_resource'] - c2 = ( (5 == v['vcpus']) and (20480 == v['memory_mb']) and (876 == v['local_gb'])) - c3 = ( 2 == len(ret['usage']) ) - c4 = ( self.instance1['project_id'] in ret['usage'].keys()) - c5 = ( self.instance3['project_id'] in ret['usage'].keys()) - - self.assertEqual(c1 and c2 and c3 and c4 and c5, True) - - - # ---> test for nova.scheduler.manager.has_enough_resource() - def test05(self): - """05: when cpu is exccded some instance found on dest host. """ - - db.instance_get = Mock(return_value = self.instance6) - try : - self.manager.driver.has_enough_resource(self.ctxt, 'i-12345', 'host1') - except exception.NotEmpty, e: - # dont do e.message.find(), because the below message is occured. - # DeprecationWarning: BaseException.message has been deprecated - # as of Python 2.6 - c1 = ( 0 < str(e.args).find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - - def test06(self): - """06: when memory is exccded some instance found on dest host. """ - - db.instance_get = Mock(return_value = self.instance7) - try : - self.manager.driver.has_enough_resource(self.ctxt, 'i-12345', 'host1') - except exception.NotEmpty, e: - c1 = ( 0 <= str(e.args).find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - def test07(self): - """07: when hdd is exccded some instance found on dest host. """ - - db.instance_get = Mock(return_value = self.instance8) - try : - self.manager.driver.has_enough_resource(self.ctxt, 'i-12345', 'host1') - except exception.NotEmpty, e: - c1 = ( 0 <= str(e.args).find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - - def test08(self): - """08: everything goes well. (instance_get_all_by_host returns list)""" - - ret= self.manager.driver.has_enough_resource(self.ctxt, 'i-12345', 'host1') - self.assertEqual(ret, None) - - - def test09(self): - """09: everything goes well(instance_get_all_by_host returns[]). """ - - db.instance_get_all_by_host = Mock(return_value = [] ) - ret= self.manager.driver.has_enough_resource(self.ctxt, 'i-12345', 'host1') - self.assertEqual(ret, None) - - - # ---> test for nova.scheduler.manager.live_migration() - - - def test10(self): - """10: instance_get issues NotFound. """ - - db.instance_get = Mock(side_effect=exception.NotFound("ERR")) - self.assertRaises(exception.NotFound, - self.manager.driver.schedule_live_migration, - self.ctxt, - 'i-12345', - 'host1') - - def test11(self): - """11: instance_get issues Unexpected error. """ - - db.instance_get = Mock(side_effect=TypeError("ERR")) - self.assertRaises(TypeError, - self.manager.driver.schedule_live_migration, - self.ctxt, - 'i-12345', - 'host1') - - def test12(self): - """12: instance state is not power_state.RUNNING. """ - - db.instance_get = Mock(return_value=self.instance4) - try : - self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host1') - except exception.Invalid, e: - c1 = (0 <= str(e.args).find('is not running')) - self.assertTrue(c1, True) - return False - - def test13(self): - """13: instance state_description is not running. """ - - db.instance_get = Mock(return_value=self.instance5) - try : - self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host1') - except exception.Invalid, e: - c1 = (0 <= str(e.args).find('is not running')) - self.assertTrue(c1, True) - return False - - def test14(self): - """14: dest is not compute node. - (dest is not included in the result of db.service_get_all_by_topic) - """ - try : - self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host2') - except exception.Invalid, e: - c1 = (0 <= str(e.args).find('must be compute node')) - self.assertTrue(c1, True) - return False - - def test15(self): - """ 15: dest is not alive.(service_is up returns False) """ - - self.manager.driver.service_is_up = Mock(return_value=False) - try : - self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host2') - except exception.Invalid, e: - c1 = (0 <= str(e.args).find('is not alive')) - self.assertTrue(c1, True) - return False - - # Cannot test the case of hypervisor type difference and hypervisor - # version difference, since we cannot set different mocks to same method.. - - def test16(self): - """ 16: stored "cpuinfo" is not string """ - - try : - self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host2') - except exception.Invalid, e: - c1 = (0 <= str(e.args).find('Unexpected err') ) - self.assertTrue(c1, True) - return False - - - def test17(self): - """17: rpc.call raises RemoteError(Unexpected error occurs when executing compareCPU) """ - rpc.call = Mock(return_value = rpc.RemoteError(libvirt.libvirtError, 'val', 'traceback')) - self.assertRaises(rpc.RemoteError, - self.manager.driver.schedule_live_migration, - self.ctxt, - 'i-12345', - 'host2') - - def test18(self): - """18: rpc.call returns 0 (cpu is not compatible between src and dest) """ - rpc.call = Mock(return_value = 0) - try : - self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host2') - except exception.Invalid, e: - c1 = ( 0 <= str(e.args).find('doesnt have compatibility to')) - self.assertTrue(c1, True) - return False - - def test19(self): - """19: raise NotEmpty if host doesnt have enough resource. """ - - db.instance_get = Mock(return_value = self.instance8) - try : - self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host2') - except exception.NotEmpty, e: - c1 = ( 0 <= str(e.args).find('doesnt have enough resource') ) - self.assertTrue(c1, True) - return False - - - def test20(self): - """20: everything goes well. """ - - #db.instance_get = Mock(return_value = self.instance8) - ret= self.manager.driver.schedule_live_migration(self.ctxt, 'i-12345', 'host2') - self.assertEqual(ret, self.instance8['host']) - - - def tearDown(self): - """common terminating method. """ - #sys.stdout = self.stdoutBak - pass - -if __name__ == '__main__': - #unittest.main() - suite = unittest.TestLoader().loadTestsFromTestCase(SchedulerTestFunctions) - unittest.TextTestRunner(verbosity=3).run(suite) - - diff --git a/nova/livemigration_test/UT/testCase_UT.xls b/nova/livemigration_test/UT/testCase_UT.xls deleted file mode 100644 index 2850e70f0..000000000 Binary files a/nova/livemigration_test/UT/testCase_UT.xls and /dev/null differ -- cgit From 35d3050511ef513ff440fbd9f8b44695ea8be797 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Tue, 4 Jan 2011 14:07:46 -0800 Subject: rename Easy API to Direct API --- nova/api/direct.py | 232 ++++++++++++++++++++++++++++++++++++++++++++ nova/api/easy.py | 232 -------------------------------------------- nova/tests/easy_unittest.py | 102 ------------------- nova/tests/test_direct.py | 102 +++++++++++++++++++ 4 files changed, 334 insertions(+), 334 deletions(-) create mode 100644 nova/api/direct.py delete mode 100644 nova/api/easy.py delete mode 100644 nova/tests/easy_unittest.py create mode 100644 nova/tests/test_direct.py (limited to 'nova') diff --git a/nova/api/direct.py b/nova/api/direct.py new file mode 100644 index 000000000..81b3ae202 --- /dev/null +++ b/nova/api/direct.py @@ -0,0 +1,232 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""Public HTTP interface that allows services to self-register. + +The general flow of a request is: + - Request is parsed into WSGI bits. + - Some middleware checks authentication. + - Routing takes place based on the URL to find a controller. + (/controller/method) + - Parameters are parsed from the request and passed to a method on the + controller as keyword arguments. + - Optionally 'json' is decoded to provide all the parameters. + - Actual work is done and a result is returned. + - That result is turned into json and returned. + +""" + +import inspect +import urllib + +import routes +import webob + +from nova import context +from nova import flags +from nova import utils +from nova import wsgi + + +ROUTES = {} + + +def register_service(path, handle): + ROUTES[path] = handle + + +class Router(wsgi.Router): + def __init__(self, mapper=None): + if mapper is None: + mapper = routes.Mapper() + + self._load_registered_routes(mapper) + super(Router, self).__init__(mapper=mapper) + + def _load_registered_routes(self, mapper): + for route in ROUTES: + mapper.connect('/%s/{action}' % route, + controller=ServiceWrapper(ROUTES[route])) + + +class DelegatedAuthMiddleware(wsgi.Middleware): + def process_request(self, request): + os_user = request.headers['X-OpenStack-User'] + os_project = request.headers['X-OpenStack-Project'] + context_ref = context.RequestContext(user=os_user, project=os_project) + request.environ['openstack.context'] = context_ref + + +class JsonParamsMiddleware(wsgi.Middleware): + def process_request(self, request): + if 'json' not in request.params: + return + + params_json = request.params['json'] + params_parsed = utils.loads(params_json) + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ['openstack.params'] = params + + +class PostParamsMiddleware(wsgi.Middleware): + def process_request(self, request): + params_parsed = request.params + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ['openstack.params'] = params + + +class Reflection(object): + """Reflection methods to list available methods.""" + def __init__(self): + self._methods = {} + self._controllers = {} + + def _gather_methods(self): + methods = {} + controllers = {} + for route, handler in ROUTES.iteritems(): + controllers[route] = handler.__doc__.split('\n')[0] + for k in dir(handler): + if k.startswith('_'): + continue + f = getattr(handler, k) + if not callable(f): + continue + + # bunch of ugly formatting stuff + argspec = inspect.getargspec(f) + args = [x for x in argspec[0] + if x != 'self' and x != 'context'] + defaults = argspec[3] and argspec[3] or [] + args_r = list(reversed(args)) + defaults_r = list(reversed(defaults)) + + args_out = [] + while args_r: + if defaults_r: + args_out.append((args_r.pop(0), + repr(defaults_r.pop(0)))) + else: + args_out.append((str(args_r.pop(0)),)) + + # if the method accepts keywords + if argspec[2]: + args_out.insert(0, ('**%s' % argspec[2],)) + + methods['/%s/%s' % (route, k)] = { + 'short_doc': f.__doc__.split('\n')[0], + 'doc': f.__doc__, + 'name': k, + 'args': list(reversed(args_out))} + + self._methods = methods + self._controllers = controllers + + def get_controllers(self, context): + """List available controllers.""" + if not self._controllers: + self._gather_methods() + + return self._controllers + + def get_methods(self, context): + """List available methods.""" + if not self._methods: + self._gather_methods() + + method_list = self._methods.keys() + method_list.sort() + methods = {} + for k in method_list: + methods[k] = self._methods[k]['short_doc'] + return methods + + def get_method_info(self, context, method): + """Get detailed information about a method.""" + if not self._methods: + self._gather_methods() + return self._methods[method] + + +class ServiceWrapper(wsgi.Controller): + def __init__(self, service_handle): + self.service_handle = service_handle + + @webob.dec.wsgify + def __call__(self, req): + arg_dict = req.environ['wsgiorg.routing_args'][1] + action = arg_dict['action'] + del arg_dict['action'] + + context = req.environ['openstack.context'] + # allow middleware up the stack to override the params + params = {} + if 'openstack.params' in req.environ: + params = req.environ['openstack.params'] + + # TODO(termie): do some basic normalization on methods + method = getattr(self.service_handle, action) + + result = method(context, **params) + if type(result) is dict or type(result) is list: + return self._serialize(result, req) + else: + return result + + +class Proxy(object): + """Pretend a Direct API endpoint is an object.""" + def __init__(self, app, prefix=None): + self.app = app + self.prefix = prefix + + def __do_request(self, path, context, **kwargs): + req = webob.Request.blank(path) + req.method = 'POST' + req.body = urllib.urlencode({'json': utils.dumps(kwargs)}) + req.environ['openstack.context'] = context + resp = req.get_response(self.app) + try: + return utils.loads(resp.body) + except Exception: + return resp.body + + def __getattr__(self, key): + if self.prefix is None: + return self.__class__(self.app, prefix=key) + + def _wrapper(context, **kwargs): + return self.__do_request('/%s/%s' % (self.prefix, key), + context, + **kwargs) + _wrapper.func_name = key + return _wrapper diff --git a/nova/api/easy.py b/nova/api/easy.py deleted file mode 100644 index 7468e3115..000000000 --- a/nova/api/easy.py +++ /dev/null @@ -1,232 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -"""Public HTTP interface that allows services to self-register. - -The general flow of a request is: - - Request is parsed into WSGI bits. - - Some middleware checks authentication. - - Routing takes place based on the URL to find a controller. - (/controller/method) - - Parameters are parsed from the request and passed to a method on the - controller as keyword arguments. - - Optionally 'json' is decoded to provide all the parameters. - - Actual work is done and a result is returned. - - That result is turned into json and returned. - -""" - -import inspect -import urllib - -import routes -import webob - -from nova import context -from nova import flags -from nova import utils -from nova import wsgi - - -EASY_ROUTES = {} - - -def register_service(path, handle): - EASY_ROUTES[path] = handle - - -class DelegatedAuthMiddleware(wsgi.Middleware): - def process_request(self, request): - os_user = request.headers['X-OpenStack-User'] - os_project = request.headers['X-OpenStack-Project'] - context_ref = context.RequestContext(user=os_user, project=os_project) - request.environ['openstack.context'] = context_ref - - -class JsonParamsMiddleware(wsgi.Middleware): - def process_request(self, request): - if 'json' not in request.params: - return - - params_json = request.params['json'] - params_parsed = utils.loads(params_json) - params = {} - for k, v in params_parsed.iteritems(): - if k in ('self', 'context'): - continue - if k.startswith('_'): - continue - params[k] = v - - request.environ['openstack.params'] = params - - -class ReqParamsMiddleware(wsgi.Middleware): - def process_request(self, request): - params_parsed = request.params - params = {} - for k, v in params_parsed.iteritems(): - if k in ('self', 'context'): - continue - if k.startswith('_'): - continue - params[k] = v - - request.environ['openstack.params'] = params - - -class SundayMorning(wsgi.Router): - def __init__(self, mapper=None): - if mapper is None: - mapper = routes.Mapper() - - self._load_registered_routes(mapper) - super(SundayMorning, self).__init__(mapper=mapper) - - def _load_registered_routes(self, mapper): - for route in EASY_ROUTES: - mapper.connect('/%s/{action}' % route, - controller=ServiceWrapper(EASY_ROUTES[route])) - - -class Reflection(object): - """Reflection methods to list available methods.""" - def __init__(self): - self._methods = {} - self._controllers = {} - - def _gather_methods(self): - methods = {} - controllers = {} - for route, handler in EASY_ROUTES.iteritems(): - controllers[route] = handler.__doc__.split('\n')[0] - for k in dir(handler): - if k.startswith('_'): - continue - f = getattr(handler, k) - if not callable(f): - continue - - # bunch of ugly formatting stuff - argspec = inspect.getargspec(f) - args = [x for x in argspec[0] - if x != 'self' and x != 'context'] - defaults = argspec[3] and argspec[3] or [] - args_r = list(reversed(args)) - defaults_r = list(reversed(defaults)) - - args_out = [] - while args_r: - if defaults_r: - args_out.append((args_r.pop(0), - repr(defaults_r.pop(0)))) - else: - args_out.append((str(args_r.pop(0)),)) - - # if the method accepts keywords - if argspec[2]: - args_out.insert(0, ('**%s' % argspec[2],)) - - methods['/%s/%s' % (route, k)] = { - 'short_doc': f.__doc__.split('\n')[0], - 'doc': f.__doc__, - 'name': k, - 'args': list(reversed(args_out))} - - self._methods = methods - self._controllers = controllers - - def get_controllers(self, context): - """List available controllers.""" - if not self._controllers: - self._gather_methods() - - return self._controllers - - def get_methods(self, context): - """List available methods.""" - if not self._methods: - self._gather_methods() - - method_list = self._methods.keys() - method_list.sort() - methods = {} - for k in method_list: - methods[k] = self._methods[k]['short_doc'] - return methods - - def get_method_info(self, context, method): - """Get detailed information about a method.""" - if not self._methods: - self._gather_methods() - return self._methods[method] - - -class ServiceWrapper(wsgi.Controller): - def __init__(self, service_handle): - self.service_handle = service_handle - - @webob.dec.wsgify - def __call__(self, req): - arg_dict = req.environ['wsgiorg.routing_args'][1] - action = arg_dict['action'] - del arg_dict['action'] - - context = req.environ['openstack.context'] - # allow middleware up the stack to override the params - params = {} - if 'openstack.params' in req.environ: - params = req.environ['openstack.params'] - - # TODO(termie): do some basic normalization on methods - method = getattr(self.service_handle, action) - - result = method(context, **params) - if type(result) is dict or type(result) is list: - return self._serialize(result, req) - else: - return result - - -class Proxy(object): - """Pretend an Easy API endpoint is an object.""" - def __init__(self, app, prefix=None): - self.app = app - self.prefix = prefix - - def __do_request(self, path, context, **kwargs): - req = webob.Request.blank(path) - req.method = 'POST' - req.body = urllib.urlencode({'json': utils.dumps(kwargs)}) - req.environ['openstack.context'] = context - resp = req.get_response(self.app) - try: - return utils.loads(resp.body) - except Exception: - return resp.body - - def __getattr__(self, key): - if self.prefix is None: - return self.__class__(self.app, prefix=key) - - def _wrapper(context, **kwargs): - return self.__do_request('/%s/%s' % (self.prefix, key), - context, - **kwargs) - _wrapper.func_name = key - return _wrapper diff --git a/nova/tests/easy_unittest.py b/nova/tests/easy_unittest.py deleted file mode 100644 index cd13c7710..000000000 --- a/nova/tests/easy_unittest.py +++ /dev/null @@ -1,102 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -"""Tests for Easy API.""" - -import json -import logging - -import webob - -from nova import context -from nova import exception -from nova import test -from nova import utils -from nova.api import easy -from nova.compute import api as compute_api -from nova.tests import cloud_unittest - - -class FakeService(object): - def echo(self, context, data): - return {'data': data} - - def context(self, context): - return {'user': context.user_id, - 'project': context.project_id} - - -class EasyTestCase(test.TestCase): - def setUp(self): - super(EasyTestCase, self).setUp() - easy.register_service('fake', FakeService()) - self.router = easy.ReqParamsMiddleware( - easy.JsonParamsMiddleware( - easy.SundayMorning())) - self.auth_router = easy.DelegatedAuthMiddleware(self.router) - self.context = context.RequestContext('user1', 'proj1') - - def tearDown(self): - easy.EASY_ROUTES = {} - - def test_delegated_auth(self): - req = webob.Request.blank('/fake/context') - req.headers['X-OpenStack-User'] = 'user1' - req.headers['X-OpenStack-Project'] = 'proj1' - resp = req.get_response(self.auth_router) - data = json.loads(resp.body) - self.assertEqual(data['user'], 'user1') - self.assertEqual(data['project'], 'proj1') - - def test_json_params(self): - req = webob.Request.blank('/fake/echo') - req.environ['openstack.context'] = self.context - req.method = 'POST' - req.body = 'json=%s' % json.dumps({'data': 'foo'}) - resp = req.get_response(self.router) - resp_parsed = json.loads(resp.body) - self.assertEqual(resp_parsed['data'], 'foo') - - def test_req_params(self): - req = webob.Request.blank('/fake/echo') - req.environ['openstack.context'] = self.context - req.method = 'POST' - req.body = 'data=foo' - resp = req.get_response(self.router) - resp_parsed = json.loads(resp.body) - self.assertEqual(resp_parsed['data'], 'foo') - - def test_proxy(self): - proxy = easy.Proxy(self.router) - rv = proxy.fake.echo(self.context, data='baz') - self.assertEqual(rv['data'], 'baz') - - -class EasyCloudTestCase(cloud_unittest.CloudTestCase): - def setUp(self): - super(EasyCloudTestCase, self).setUp() - compute_handle = compute_api.ComputeAPI(self.cloud.network_manager, - self.cloud.image_service) - easy.register_service('compute', compute_handle) - self.router = easy.JsonParamsMiddleware(easy.SundayMorning()) - proxy = easy.Proxy(self.router) - self.cloud.compute_api = proxy.compute - - def tearDown(self): - super(EasyCloudTestCase, self).tearDown() - easy.EASY_ROUTES = {} diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py new file mode 100644 index 000000000..d73c64ce0 --- /dev/null +++ b/nova/tests/test_direct.py @@ -0,0 +1,102 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""Tests for Direct API.""" + +import json +import logging + +import webob + +from nova import context +from nova import exception +from nova import test +from nova import utils +from nova.api import direct +from nova.compute import api as compute_api +from nova.tests import cloud_unittest + + +class FakeService(object): + def echo(self, context, data): + return {'data': data} + + def context(self, context): + return {'user': context.user_id, + 'project': context.project_id} + + +class DirectTestCase(test.TestCase): + def setUp(self): + super(DirectTestCase, self).setUp() + direct.register_service('fake', FakeService()) + self.router = direct.PostParamsMiddleware( + direct.JsonParamsMiddleware( + direct.Router())) + self.auth_router = direct.DelegatedAuthMiddleware(self.router) + self.context = context.RequestContext('user1', 'proj1') + + def tearDown(self): + direct.ROUTES = {} + + def test_delegated_auth(self): + req = webob.Request.blank('/fake/context') + req.headers['X-OpenStack-User'] = 'user1' + req.headers['X-OpenStack-Project'] = 'proj1' + resp = req.get_response(self.auth_router) + data = json.loads(resp.body) + self.assertEqual(data['user'], 'user1') + self.assertEqual(data['project'], 'proj1') + + def test_json_params(self): + req = webob.Request.blank('/fake/echo') + req.environ['openstack.context'] = self.context + req.method = 'POST' + req.body = 'json=%s' % json.dumps({'data': 'foo'}) + resp = req.get_response(self.router) + resp_parsed = json.loads(resp.body) + self.assertEqual(resp_parsed['data'], 'foo') + + def test_req_params(self): + req = webob.Request.blank('/fake/echo') + req.environ['openstack.context'] = self.context + req.method = 'POST' + req.body = 'data=foo' + resp = req.get_response(self.router) + resp_parsed = json.loads(resp.body) + self.assertEqual(resp_parsed['data'], 'foo') + + def test_proxy(self): + proxy = direct.Proxy(self.router) + rv = proxy.fake.echo(self.context, data='baz') + self.assertEqual(rv['data'], 'baz') + + +class DirectCloudTestCase(cloud_unittest.CloudTestCase): + def setUp(self): + super(DirectCloudTestCase, self).setUp() + compute_handle = compute_api.ComputeAPI(self.cloud.network_manager, + self.cloud.image_service) + direct.register_service('compute', compute_handle) + self.router = direct.JsonParamsMiddleware(direct.Router()) + proxy = direct.Proxy(self.router) + self.cloud.compute_api = proxy.compute + + def tearDown(self): + super(DirectCloudTestCase, self).tearDown() + direct.ROUTES = {} -- cgit From f55dbc2f599ed56fb59c7f7a94cd81d3fd82c8dd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:01:29 -0500 Subject: Rework how routing is done in ec2 endpoint. --- nova/api/ec2/__init__.py | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index aa3bfaeb4..a5810479e 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -29,6 +29,7 @@ import webob.exc from nova import context from nova import exception from nova import flags +from nova import utils from nova import wsgi from nova.api.ec2 import apirequest from nova.api.ec2 import admin @@ -157,6 +158,31 @@ class Authenticate(wsgi.Middleware): return self.application +class Requestify(wsgi.Middleware): + + def __init__(self, app, controller_name): + super(Requestify, self).__init__(app) + self.controller = utils.import_class(controller_name)() + + @webob.dec.wsgify + def __call__(self, req): + non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', + 'SignatureVersion', 'Version', 'Timestamp'] + args = dict(req.params) + try: + # Raise KeyError if omitted + action = req.params['Action'] + for non_arg in non_args: + # Remove, but raise KeyError if omitted + args.pop(non_arg) + except: + raise webob.exc.HTTPBadRequest() + api_request = apirequest.APIRequest(self.controller, action) + req.environ['ec2.request'] = api_request + req.environ['ec2.action_args'] = args + return self.application + + class Router(wsgi.Middleware): """Add ec2.'controller', .'action', and .'action_args' to WSGI environ.""" @@ -256,10 +282,9 @@ class Authorizer(wsgi.Middleware): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - controller_name = req.environ['ec2.controller'].__class__.__name__ - action = req.environ['ec2.action'] - allowed_roles = self.action_roles[controller_name].get(action, - ['none']) + controller = req.environ['ec2.request'].controller.__class__.__name__ + action = req.environ['ec2.request'].action + allowed_roles = self.action_roles[controller].get(action, ['none']) if self._matches_any_role(context, allowed_roles): return self.application else: @@ -289,11 +314,8 @@ class Executor(wsgi.Application): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - controller = req.environ['ec2.controller'] - action = req.environ['ec2.action'] args = req.environ['ec2.action_args'] - - api_request = apirequest.APIRequest(controller, action) + api_request = req.environ['ec2.request'] result = None try: result = api_request.send(context, **args) @@ -369,3 +391,8 @@ def executor_factory(global_args, **local_args): def versions_factory(global_args, **local_args): return Versions() + +def requestify_factory(global_args, **local_args): + def requestifier(app): + return Requestify(app, local_args['controller']) + return requestifier -- cgit From 2491c2484f025cb3f061fcc6a5c6915006feb47b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:16:16 -0500 Subject: Make paste the default api pattern. * get rid of the --use_lockout flag since it will be specified in paste config (Example line is commented out in etc/nova-api.conf, factory is in place) * remove old nova-api binary and promote nova-api-paste * change how we store ec2 parameters to bin the the ApiRequest * get rid of Router, since paste.urlmap is equally effective (Requestify now gets passed the name of the controller requests are to.) --- nova/api/ec2/__init__.py | 74 +++++----------------------------------------- nova/api/ec2/apirequest.py | 7 +++-- 2 files changed, 11 insertions(+), 70 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index a5810479e..2f8f4f272 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -21,7 +21,6 @@ Starting point for routing EC2 requests. """ import logging -import routes import webob import webob.dec import webob.exc @@ -32,8 +31,6 @@ from nova import flags from nova import utils from nova import wsgi from nova.api.ec2 import apirequest -from nova.api.ec2 import admin -from nova.api.ec2 import cloud from nova.auth import manager @@ -41,8 +38,6 @@ FLAGS = flags.FLAGS flags.DEFINE_boolean('use_forwarded_for', False, 'Treat X-Forwarded-For as the canonical remote address. ' 'Only enable this if you have a sanitizing proxy.') -flags.DEFINE_boolean('use_lockout', False, - 'Whether or not to use lockout middleware.') flags.DEFINE_integer('lockout_attempts', 5, 'Number of failed auths before lockout.') flags.DEFINE_integer('lockout_minutes', 15, @@ -57,15 +52,6 @@ _log = logging.getLogger("api") _log.setLevel(logging.DEBUG) -class API(wsgi.Middleware): - """Routing for all EC2 API requests.""" - - def __init__(self): - self.application = Authenticate(Router(Authorizer(Executor()))) - if FLAGS.use_lockout: - self.application = Lockout(self.application) - - class Lockout(wsgi.Middleware): """Lockout for x minutes on y failed auths in a z minute period. @@ -177,55 +163,12 @@ class Requestify(wsgi.Middleware): args.pop(non_arg) except: raise webob.exc.HTTPBadRequest() - api_request = apirequest.APIRequest(self.controller, action) + api_request = apirequest.APIRequest(self.controller, action, args) req.environ['ec2.request'] = api_request req.environ['ec2.action_args'] = args return self.application -class Router(wsgi.Middleware): - - """Add ec2.'controller', .'action', and .'action_args' to WSGI environ.""" - - def __init__(self, application): - super(Router, self).__init__(application) - self.map = routes.Mapper() - self.map.connect("/{controller_name}/") - self.controllers = dict(Cloud=cloud.CloudController(), - Admin=admin.AdminController()) - - @webob.dec.wsgify - def __call__(self, req): - # Obtain the appropriate controller and action for this request. - try: - match = self.map.match(req.path_info) - controller_name = match['controller_name'] - controller = self.controllers[controller_name] - except: - raise webob.exc.HTTPNotFound() - non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', - 'SignatureVersion', 'Version', 'Timestamp'] - args = dict(req.params) - try: - # Raise KeyError if omitted - action = req.params['Action'] - for non_arg in non_args: - # Remove, but raise KeyError if omitted - args.pop(non_arg) - except: - raise webob.exc.HTTPBadRequest() - - _log.debug(_('action: %s') % action) - for key, value in args.items(): - _log.debug(_('arg: %s\t\tval: %s') % (key, value)) - - # Success! - req.environ['ec2.controller'] = controller - req.environ['ec2.action'] = action - req.environ['ec2.action_args'] = args - return self.application - - class Authorizer(wsgi.Middleware): """Authorize an EC2 API request. @@ -314,13 +257,11 @@ class Executor(wsgi.Application): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - args = req.environ['ec2.action_args'] api_request = req.environ['ec2.request'] result = None try: - result = api_request.send(context, **args) + result = api_request.invoke(context) except exception.ApiError as ex: - if ex.code: return self._error(req, ex.code, ex.message) else: @@ -373,12 +314,6 @@ def authenticate_factory(global_args, **local_args): return authenticator -def router_factory(global_args, **local_args): - def router(app): - return Router(app) - return router - - def authorizer_factory(global_args, **local_args): def authorizer(app): return Authorizer(app) @@ -396,3 +331,8 @@ def requestify_factory(global_args, **local_args): def requestifier(app): return Requestify(app, local_args['controller']) return requestifier + +def lockout_factory(global_args, **local_args): + def locksmith(app): + return Lockout(app) + return locksmith diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index a90fbeb0c..8a1dd3978 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -83,11 +83,12 @@ def _try_convert(value): class APIRequest(object): - def __init__(self, controller, action): + def __init__(self, controller, action, args): self.controller = controller self.action = action + self.args = args - def send(self, context, **kwargs): + def invoke(self, context): try: method = getattr(self.controller, _camelcase_to_underscore(self.action)) @@ -100,7 +101,7 @@ class APIRequest(object): raise Exception(_error) args = {} - for key, value in kwargs.items(): + for key, value in self.args.items(): parts = key.split(".") key = _camelcase_to_underscore(parts[0]) if isinstance(value, str) or isinstance(value, unicode): -- cgit From 6dc2e665b5b6f690882e6029984a11dc7063b437 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:33:17 -0500 Subject: remove unused nova/api/__init__.py --- nova/api/__init__.py | 111 --------------------------------------------------- 1 file changed, 111 deletions(-) delete mode 100644 nova/api/__init__.py (limited to 'nova') diff --git a/nova/api/__init__.py b/nova/api/__init__.py deleted file mode 100644 index 26fed847b..000000000 --- a/nova/api/__init__.py +++ /dev/null @@ -1,111 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. -""" -Root WSGI middleware for all API controllers. - -**Related Flags** - -:osapi_subdomain: subdomain running the OpenStack API (default: api) -:ec2api_subdomain: subdomain running the EC2 API (default: ec2) - -""" -import logging - -import routes -import webob.dec - -from nova import flags -from nova import wsgi -from nova.api import ec2 -from nova.api import openstack -from nova.api.ec2 import metadatarequesthandler - - -flags.DEFINE_string('osapi_subdomain', 'api', - 'subdomain running the OpenStack API') -flags.DEFINE_string('ec2api_subdomain', 'ec2', - 'subdomain running the EC2 API') - -FLAGS = flags.FLAGS - - -class API(wsgi.Router): - """Routes top-level requests to the appropriate controller.""" - - def __init__(self, default_api): - osapi_subdomain = {'sub_domain': [FLAGS.osapi_subdomain]} - ec2api_subdomain = {'sub_domain': [FLAGS.ec2api_subdomain]} - if default_api == 'os': - osapi_subdomain = {} - elif default_api == 'ec2': - ec2api_subdomain = {} - mapper = routes.Mapper() - mapper.sub_domains = True - - mapper.connect("/", controller=self.osapi_versions, - conditions=osapi_subdomain) - mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(), - conditions=osapi_subdomain) - - mapper.connect("/", controller=self.ec2api_versions, - conditions=ec2api_subdomain) - mapper.connect("/services/{path_info:.*}", controller=ec2.API(), - conditions=ec2api_subdomain) - mrh = metadatarequesthandler.MetadataRequestHandler() - for s in ['/latest', - '/2009-04-04', - '/2008-09-01', - '/2008-02-01', - '/2007-12-15', - '/2007-10-10', - '/2007-08-29', - '/2007-03-01', - '/2007-01-19', - '/1.0']: - mapper.connect('%s/{path_info:.*}' % s, controller=mrh, - conditions=ec2api_subdomain) - - super(API, self).__init__(mapper) - - @webob.dec.wsgify - def osapi_versions(self, req): - """Respond to a request for all OpenStack API versions.""" - response = { - "versions": [ - dict(status="CURRENT", id="v1.0")]} - metadata = { - "application/xml": { - "attributes": dict(version=["status", "id"])}} - return wsgi.Serializer(req.environ, metadata).to_content_type(response) - - @webob.dec.wsgify - def ec2api_versions(self, req): - """Respond to a request for all EC2 versions.""" - # available api versions - versions = [ - '1.0', - '2007-01-19', - '2007-03-01', - '2007-08-29', - '2007-10-10', - '2007-12-15', - '2008-02-01', - '2008-09-01', - '2009-04-04', - ] - return ''.join('%s\n' % v for v in versions) -- cgit From 406c8cdf027b13636ab3c8fa609aabe929057d6f Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:46:12 -0500 Subject: Remove flags and unused API class from openstack api, since such things are specified in paste config now. --- nova/api/openstack/__init__.py | 29 ----------------------------- 1 file changed, 29 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index a1430caed..c5de03a09 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -43,40 +43,11 @@ from nova.api.openstack import sharedipgroups FLAGS = flags.FLAGS -flags.DEFINE_string('os_api_auth', - 'nova.api.openstack.auth.AuthMiddleware', - 'The auth mechanism to use for the OpenStack API implemenation') - -flags.DEFINE_string('os_api_ratelimiting', - 'nova.api.openstack.ratelimiting.RateLimitingMiddleware', - 'Default ratelimiting implementation for the Openstack API') - flags.DEFINE_bool('allow_admin_api', False, 'When True, this API service will accept admin operations.') -class API(wsgi.Middleware): - """WSGI entry point for all OpenStack API requests.""" - - def __init__(self): - auth_middleware = utils.import_class(FLAGS.os_api_auth) - ratelimiting_middleware = \ - utils.import_class(FLAGS.os_api_ratelimiting) - app = auth_middleware(ratelimiting_middleware(APIRouter())) - super(API, self).__init__(app) - - @webob.dec.wsgify - def __call__(self, req): - try: - return req.get_response(self.application) - except Exception as ex: - logging.warn(_("Caught error: %s") % str(ex)) - logging.error(traceback.format_exc()) - exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) - return faults.Fault(exc) - - class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller -- cgit From 8926f33d4da9def15dde68a5a15fd9477aee6452 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:02:56 -0500 Subject: pep8 fixes. --- nova/api/ec2/__init__.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 2f8f4f272..dfa919e07 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -327,11 +327,13 @@ def executor_factory(global_args, **local_args): def versions_factory(global_args, **local_args): return Versions() + def requestify_factory(global_args, **local_args): def requestifier(app): return Requestify(app, local_args['controller']) return requestifier + def lockout_factory(global_args, **local_args): def locksmith(app): return Lockout(app) -- cgit From a05edf5eebf093f6f1b48a9fcbeaf8a9ae7b3899 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:13:37 -0500 Subject: Make test_access use ec2.request instead of .controller and .action. --- nova/tests/test_access.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_access.py b/nova/tests/test_access.py index 58fdea3b5..e170ccee6 100644 --- a/nova/tests/test_access.py +++ b/nova/tests/test_access.py @@ -17,25 +17,34 @@ # under the License. import unittest -import logging import webob from nova import context -from nova import exception from nova import flags from nova import test from nova.api import ec2 from nova.auth import manager - FLAGS = flags.FLAGS -class Context(object): +class FakeControllerClass(object): pass +class FakeApiRequest(object): + def __init__(self, action): + self.controller = FakeControllerClass() + self.action = action + + class AccessTestCase(test.TestCase): + def _env_for(self, ctxt, action): + env = {} + env['ec2.context'] = ctxt + env['ec2.request'] = FakeApiRequest(action) + return env + def setUp(self): super(AccessTestCase, self).setUp() um = manager.AuthManager() @@ -65,7 +74,7 @@ class AccessTestCase(test.TestCase): return [''] self.mw = ec2.Authorizer(noopWSGIApp) - self.mw.action_roles = {'str': { + self.mw.action_roles = {'FakeControllerClass': { '_allow_all': ['all'], '_allow_none': [], '_allow_project_manager': ['projectmanager'], @@ -85,9 +94,7 @@ class AccessTestCase(test.TestCase): def response_status(self, user, methodName): ctxt = context.RequestContext(user, self.project) - environ = {'ec2.context': ctxt, - 'ec2.controller': 'some string', - 'ec2.action': methodName} + environ = self._env_for(ctxt, methodName) req = webob.Request.blank('/', environ) resp = req.get_response(self.mw) return resp.status_int -- cgit From f62a010717c3ac66284948870f9c8d8216e4221b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:42:56 -0500 Subject: Build app manually for test_api since nova.ec2.API is gone. --- nova/tests/test_api.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index 33d4cb294..44894fd0b 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -26,9 +26,8 @@ import StringIO import webob from nova import context -from nova import flags from nova import test -from nova import api +from nova.api import ec2 from nova.api.ec2 import cloud from nova.api.ec2 import apirequest from nova.auth import manager @@ -100,12 +99,10 @@ class ApiEc2TestCase(test.TrialTestCase): """Unit test for the cloud controller on an EC2 API""" def setUp(self): super(ApiEc2TestCase, self).setUp() - self.manager = manager.AuthManager() - self.host = '127.0.0.1' - - self.app = api.API('ec2') + self.app = ec2.Authenticate(ec2.Requestify(ec2.Executor(), + 'nova.api.ec2.cloud.CloudController')) def expect_http(self, host=None, is_secure=False): """Returns a new EC2 connection""" -- cgit From 6f855be07afb598090184bacf6d709191012c807 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:50:17 -0500 Subject: Declare a flag for test to run in isolation. --- nova/tests/test_compute.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 1fb9143f1..e6a0ffa20 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -33,6 +33,7 @@ from nova.compute import api as compute_api FLAGS = flags.FLAGS +flags.DECLARE('stub_network', 'nova.compute.manager') class ComputeTestCase(test.TestCase): -- cgit From bccec6c8bac90517a972a5eb8bb91a82b3a13065 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 02:08:01 -0500 Subject: Fix openstack api tests and add a FaultWrapper to turn exceptions to faults. --- nova/api/openstack/__init__.py | 24 +++++++++++++++---- nova/tests/api/openstack/fakes.py | 13 ++++++++++ nova/tests/api/openstack/test_adminapi.py | 10 +++++--- nova/tests/api/openstack/test_api.py | 26 +++++++++++++------- nova/tests/api/openstack/test_auth.py | 18 +++++++------- nova/tests/api/openstack/test_flavors.py | 2 +- nova/tests/api/openstack/test_images.py | 4 ++-- nova/tests/api/openstack/test_servers.py | 40 +++++++++++++++---------------- 8 files changed, 88 insertions(+), 49 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index c5de03a09..21b9b1d7d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -20,8 +20,6 @@ WSGI middleware for OpenStack API controllers. """ -import time - import logging import routes import traceback @@ -29,15 +27,12 @@ import webob.dec import webob.exc import webob -from nova import context from nova import flags -from nova import utils from nova import wsgi from nova.api.openstack import faults from nova.api.openstack import backup_schedules from nova.api.openstack import flavors from nova.api.openstack import images -from nova.api.openstack import ratelimiting from nova.api.openstack import servers from nova.api.openstack import sharedipgroups @@ -48,6 +43,19 @@ flags.DEFINE_bool('allow_admin_api', 'When True, this API service will accept admin operations.') +class FaultWrapper(wsgi.Middleware): + """Calls down the middleware stack, making exceptions into faults.""" + + @webob.dec.wsgify + def __call__(self, req): + try: + return req.get_response(self.application) + except Exception as ex: + logging.warn(_("Caught error: %s") % str(ex)) + logging.error(traceback.format_exc()) + exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) + return faults.Fault(exc) + class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller @@ -105,3 +113,9 @@ def router_factory(global_cof, **local_conf): def versions_factory(global_conf, **local_conf): return Versions() + + +def fault_wrapper_factory(global_conf, **local_conf): + def fwrap(app): + return FaultWrapper(app) + return fwrap diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 961431154..2028024bb 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -22,6 +22,7 @@ import string import webob import webob.dec +from paste import urlmap from nova import auth from nova import context @@ -29,6 +30,7 @@ from nova import exception as exc from nova import flags from nova import utils import nova.api.openstack.auth +from nova.api import openstack from nova.api.openstack import auth from nova.api.openstack import ratelimiting from nova.image import glance @@ -69,6 +71,17 @@ def fake_wsgi(self, req): return self.application +def wsgi_app(inner_application=None): + if not inner_application: + inner_application = openstack.APIRouter() + mapper = urlmap.URLMap() + api = openstack.FaultWrapper(auth.AuthMiddleware( + ratelimiting.RateLimitingMiddleware(inner_application))) + mapper['/v1.0'] = api + mapper['/'] = openstack.FaultWrapper(openstack.Versions()) + return mapper + + def stub_out_key_pair_funcs(stubs): def key_pair(context, user_id): return [dict(name='key', public_key='public_key')] diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py index 1b2e1654d..73120c31d 100644 --- a/nova/tests/api/openstack/test_adminapi.py +++ b/nova/tests/api/openstack/test_adminapi.py @@ -19,15 +19,19 @@ import unittest import stubout import webob +from paste import urlmap -import nova.api from nova import flags +from nova.api import openstack +from nova.api.openstack import ratelimiting +from nova.api.openstack import auth from nova.tests.api.openstack import fakes FLAGS = flags.FLAGS class AdminAPITest(unittest.TestCase): + def setUp(self): self.stubs = stubout.StubOutForTesting() fakes.FakeAuthManager.auth_data = {} @@ -45,7 +49,7 @@ class AdminAPITest(unittest.TestCase): FLAGS.allow_admin_api = True # We should still be able to access public operations. req = webob.Request.blank('/v1.0/flavors') - res = req.get_response(nova.api.API('os')) + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) # TODO: Confirm admin operations are available. @@ -53,7 +57,7 @@ class AdminAPITest(unittest.TestCase): FLAGS.allow_admin_api = False # We should still be able to access public operations. req = webob.Request.blank('/v1.0/flavors') - res = req.get_response(nova.api.API('os')) + res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) # TODO: Confirm admin operations are unavailable. diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py index d8b202e21..1474148f5 100644 --- a/nova/tests/api/openstack/test_api.py +++ b/nova/tests/api/openstack/test_api.py @@ -19,14 +19,18 @@ import unittest import webob.exc import webob.dec -import nova.api.openstack -from nova.api.openstack import API -from nova.api.openstack import faults from webob import Request +from nova.api import openstack +from nova.api.openstack import faults + class APITest(unittest.TestCase): + def _wsgi_app(self, inner_app): + # simpler version of the app than fakes.wsgi_app + return openstack.FaultWrapper(inner_app) + def test_exceptions_are_converted_to_faults(self): @webob.dec.wsgify @@ -46,29 +50,33 @@ class APITest(unittest.TestCase): exc = webob.exc.HTTPNotFound(explanation='Raised a webob.exc') return faults.Fault(exc) - api = API() - api.application = succeed + #api.application = succeed + api = self._wsgi_app(succeed) resp = Request.blank('/').get_response(api) self.assertFalse('computeFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 200, resp.body) - api.application = raise_webob_exc + #api.application = raise_webob_exc + api = self._wsgi_app(raise_webob_exc) resp = Request.blank('/').get_response(api) self.assertFalse('computeFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 404, resp.body) - api.application = raise_api_fault + #api.application = raise_api_fault + api = self._wsgi_app(raise_api_fault) resp = Request.blank('/').get_response(api) self.assertTrue('itemNotFound' in resp.body, resp.body) self.assertEqual(resp.status_int, 404, resp.body) - api.application = fail + #api.application = fail + api = self._wsgi_app(fail) resp = Request.blank('/').get_response(api) self.assertTrue('{"computeFault' in resp.body, resp.body) self.assertEqual(resp.status_int, 500, resp.body) - api.application = fail + #api.application = fail + api = self._wsgi_app(fail) resp = Request.blank('/.xml').get_response(api) self.assertTrue(' Date: Thu, 6 Jan 2011 13:17:28 -0500 Subject: Add blank __init__ file for fixing importability. The stale .pyc masked this error locally. --- nova/api/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 nova/api/__init__.py (limited to 'nova') diff --git a/nova/api/__init__.py b/nova/api/__init__.py new file mode 100644 index 000000000..0fedbbfad --- /dev/null +++ b/nova/api/__init__.py @@ -0,0 +1,19 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +"""No-op __init__ for directory full of api goodies.""" -- cgit From 963ece6feac200151b35df2efa0df4b1c75f1763 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 13:18:17 -0500 Subject: Add factories into the wsgi classes. --- nova/wsgi.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/wsgi.py b/nova/wsgi.py index b5d6b96c1..5ecc21eed 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -63,10 +63,20 @@ class Server(object): class Application(object): -# TODO(gundlach): I think we should toss this class, now that it has no -# purpose. """Base WSGI application wrapper. Subclasses need to implement __call__.""" + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config fles.""" + rv = cls() + for k,v in local_config.iteritems(): + if hasattr(rv, k): + setattr(rv, k, v) + else: + logging.debug(_("Unknown local config option %s for %s"), + k, cls) + return rv + def __call__(self, environ, start_response): r"""Subclasses will probably want to implement __call__ like this: @@ -111,6 +121,20 @@ class Middleware(Application): behavior. """ + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config fles.""" + def _factory(app): + rv = cls(app) + for k,v in local_config.iteritems(): + if hasattr(rv, k): + setattr(rv, k, v) + else: + logging.debug(_("Unknown local config option %s for %s"), + k, cls) + return rv + return _factory + def __init__(self, application): # pylint: disable-msg=W0231 self.application = application -- cgit From 4e034f3d69c6aba6920dd7dd38e07aeb495b45db Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 13:57:48 -0500 Subject: Remove module-level factory methods in favor of having a factory class-method on wsgi components themselves. Local options from config are passed to the __init__ method of the component as kwargs. --- nova/api/ec2/__init__.py | 36 +---------------- nova/api/ec2/metadatarequesthandler.py | 7 +--- nova/api/openstack/__init__.py | 19 +++------ nova/api/openstack/auth.py | 6 --- nova/api/openstack/ratelimiting/__init__.py | 6 --- nova/wsgi.py | 62 ++++++++++++++++++++--------- 6 files changed, 53 insertions(+), 83 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index dfa919e07..0836c3411 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -146,9 +146,9 @@ class Authenticate(wsgi.Middleware): class Requestify(wsgi.Middleware): - def __init__(self, app, controller_name): + def __init__(self, app, controller): super(Requestify, self).__init__(app) - self.controller = utils.import_class(controller_name)() + self.controller = utils.import_class(controller)() @webob.dec.wsgify def __call__(self, req): @@ -306,35 +306,3 @@ class Versions(wsgi.Application): '2009-04-04', ] return ''.join('%s\n' % v for v in versions) - - -def authenticate_factory(global_args, **local_args): - def authenticator(app): - return Authenticate(app) - return authenticator - - -def authorizer_factory(global_args, **local_args): - def authorizer(app): - return Authorizer(app) - return authorizer - - -def executor_factory(global_args, **local_args): - return Executor() - - -def versions_factory(global_args, **local_args): - return Versions() - - -def requestify_factory(global_args, **local_args): - def requestifier(app): - return Requestify(app, local_args['controller']) - return requestifier - - -def lockout_factory(global_args, **local_args): - def locksmith(app): - return Lockout(app) - return locksmith diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py index a57a6698a..9067568a4 100644 --- a/nova/api/ec2/metadatarequesthandler.py +++ b/nova/api/ec2/metadatarequesthandler.py @@ -24,13 +24,14 @@ import webob.dec import webob.exc from nova import flags +from nova import wsgi from nova.api.ec2 import cloud FLAGS = flags.FLAGS -class MetadataRequestHandler(object): +class MetadataRequestHandler(wsgi.Application): """Serve metadata from the EC2 API.""" def print_data(self, data): @@ -79,7 +80,3 @@ class MetadataRequestHandler(object): if data is None: raise webob.exc.HTTPNotFound() return self.print_data(data) - - -def metadata_factory(global_args, **local_args): - return MetadataRequestHandler() diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 21b9b1d7d..76e3aeed9 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -62,6 +62,11 @@ class APIRouter(wsgi.Router): and method. """ + @classmethod + def factory(cls, global_config, **local_config): + """Simple paste factory, :class:`nova.wsgi.Router` doesn't have one""" + return cls() + def __init__(self): mapper = routes.Mapper() @@ -105,17 +110,3 @@ class Versions(wsgi.Application): "application/xml": { "attributes": dict(version=["status", "id"])}} return wsgi.Serializer(req.environ, metadata).to_content_type(response) - - -def router_factory(global_cof, **local_conf): - return APIRouter() - - -def versions_factory(global_conf, **local_conf): - return Versions() - - -def fault_wrapper_factory(global_conf, **local_conf): - def fwrap(app): - return FaultWrapper(app) - return fwrap diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 00e817c8d..1dfdd5318 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -134,9 +134,3 @@ class AuthMiddleware(wsgi.Middleware): token = self.db.auth_create_token(ctxt, token_dict) return token, user return None, None - - -def auth_factory(global_conf, **local_conf): - def auth(app): - return AuthMiddleware(app) - return auth diff --git a/nova/api/openstack/ratelimiting/__init__.py b/nova/api/openstack/ratelimiting/__init__.py index 81b83142f..cbb4b897e 100644 --- a/nova/api/openstack/ratelimiting/__init__.py +++ b/nova/api/openstack/ratelimiting/__init__.py @@ -219,9 +219,3 @@ class WSGIAppProxy(object): # No delay return None return float(resp.getheader('X-Wait-Seconds')) - - -def ratelimit_factory(global_conf, **local_conf): - def rl(app): - return RateLimitingMiddleware(app) - return rl diff --git a/nova/wsgi.py b/nova/wsgi.py index 5ecc21eed..aa8f315d6 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -67,15 +67,28 @@ class Application(object): @classmethod def factory(cls, global_config, **local_config): - """Used for paste app factories in paste.deploy config fles.""" - rv = cls() - for k,v in local_config.iteritems(): - if hasattr(rv, k): - setattr(rv, k, v) - else: - logging.debug(_("Unknown local config option %s for %s"), - k, cls) - return rv + """Used for paste app factories in paste.deploy config fles. + + Any local configuration (that is, values under the [app:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [app:wadl] + latest_version = 1.3 + paste.app_factory = nova.api.fancy_api:Wadl.factory + + which would result in a call to the `Wadl` class as + + import nova.api.fancy_api + fancy_api.Wadl(latest_version='1.3') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. + + """ + return cls(**local_config) def __call__(self, environ, start_response): r"""Subclasses will probably want to implement __call__ like this: @@ -123,16 +136,29 @@ class Middleware(Application): @classmethod def factory(cls, global_config, **local_config): - """Used for paste app factories in paste.deploy config fles.""" + """Used for paste app factories in paste.deploy config fles. + + Any local configuration (that is, values under the [filter:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [filter:analytics] + redis_host = 127.0.0.1 + paste.filter_factory = nova.api.analytics:Analytics.factory + + which would result in a call to the `Analytics` class as + + import nova.api.analytics + analytics.Analytics(app_from_paste, redis_host='127.0.0.1') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. + + """ def _factory(app): - rv = cls(app) - for k,v in local_config.iteritems(): - if hasattr(rv, k): - setattr(rv, k, v) - else: - logging.debug(_("Unknown local config option %s for %s"), - k, cls) - return rv + return cls(app, **local_config) return _factory def __init__(self, application): # pylint: disable-msg=W0231 -- cgit From 8003dd2f5b027491f4e171f92ccd2a1cf2946315 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 14:22:11 -0500 Subject: Pep8 --- nova/api/openstack/__init__.py | 1 + nova/tests/api/openstack/test_api.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 21b9b1d7d..452290505 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -56,6 +56,7 @@ class FaultWrapper(wsgi.Middleware): exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) return faults.Fault(exc) + class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller diff --git a/nova/tests/api/openstack/test_api.py b/nova/tests/api/openstack/test_api.py index 1474148f5..db0fe1060 100644 --- a/nova/tests/api/openstack/test_api.py +++ b/nova/tests/api/openstack/test_api.py @@ -50,7 +50,6 @@ class APITest(unittest.TestCase): exc = webob.exc.HTTPNotFound(explanation='Raised a webob.exc') return faults.Fault(exc) - #api.application = succeed api = self._wsgi_app(succeed) resp = Request.blank('/').get_response(api) -- cgit From 2997c6cd216089b569878ec93b142ee9485127ee Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 14:22:18 -0500 Subject: Remove test for removed class. --- nova/tests/api/fakes.py | 26 ---------------- nova/tests/api/test.py | 81 ------------------------------------------------- 2 files changed, 107 deletions(-) delete mode 100644 nova/tests/api/fakes.py delete mode 100644 nova/tests/api/test.py (limited to 'nova') diff --git a/nova/tests/api/fakes.py b/nova/tests/api/fakes.py deleted file mode 100644 index 0aedcaff0..000000000 --- a/nova/tests/api/fakes.py +++ /dev/null @@ -1,26 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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. - -import webob.dec -from nova import wsgi - - -class APIStub(object): - """Class to verify request and mark it was called.""" - @webob.dec.wsgify - def __call__(self, req): - return req.path_info diff --git a/nova/tests/api/test.py b/nova/tests/api/test.py deleted file mode 100644 index 9caa8c9d0..000000000 --- a/nova/tests/api/test.py +++ /dev/null @@ -1,81 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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. - -""" -Test for the root WSGI middleware for all API controllers. -""" - -import unittest - -import stubout -import webob -import webob.dec - -import nova.exception -from nova import api -from nova.tests.api.fakes import APIStub - - -class Test(unittest.TestCase): - - def setUp(self): - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.stubs.UnsetAll() - - def _request(self, url, subdomain, **kwargs): - environ_keys = {'HTTP_HOST': '%s.example.com' % subdomain} - environ_keys.update(kwargs) - req = webob.Request.blank(url, environ_keys) - return req.get_response(api.API('ec2')) - - def test_openstack(self): - self.stubs.Set(api.openstack, 'API', APIStub) - result = self._request('/v1.0/cloud', 'api') - self.assertEqual(result.body, "/cloud") - - def test_ec2(self): - self.stubs.Set(api.ec2, 'API', APIStub) - result = self._request('/services/cloud', 'ec2') - self.assertEqual(result.body, "/cloud") - - def test_not_found(self): - self.stubs.Set(api.ec2, 'API', APIStub) - self.stubs.Set(api.openstack, 'API', APIStub) - result = self._request('/test/cloud', 'ec2') - self.assertNotEqual(result.body, "/cloud") - - def test_query_api_versions(self): - result = self._request('/', 'api') - self.assertTrue('CURRENT' in result.body) - - def test_metadata(self): - def go(url): - result = self._request(url, 'ec2', REMOTE_ADDR='128.192.151.2') - # Each should get to the ORM layer and fail to find the IP - self.assertRaises(nova.exception.NotFound, go, '/latest/') - self.assertRaises(nova.exception.NotFound, go, '/2009-04-04/') - self.assertRaises(nova.exception.NotFound, go, '/1.0/') - - def test_ec2_root(self): - result = self._request('/', 'ec2') - self.assertTrue('2007-12-15\n' in result.body) - - -if __name__ == '__main__': - unittest.main() -- cgit From 3bf9bc6f6c0fbf90e3f4eab68a9bd99d85fcc422 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 6 Jan 2011 21:37:33 -0600 Subject: Reserving image before uploading --- nova/api/openstack/images.py | 10 ++- nova/compute/api.py | 10 ++- nova/compute/manager.py | 4 +- nova/image/glance.py | 157 +++---------------------------------------- nova/utils.py | 20 +++++- nova/virt/libvirt_conn.py | 2 +- nova/virt/xenapi/vm_utils.py | 24 ++++--- nova/virt/xenapi/vmops.py | 6 +- nova/virt/xenapi_conn.py | 8 ++- 9 files changed, 72 insertions(+), 169 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 867ee5a7e..4d1af77d9 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -115,7 +115,8 @@ class Controller(wsgi.Controller): items = self._service.index(req.environ['nova.context']) items = common.limited(items, req) items = [_translate_keys(item) for item in items] - items = [_translate_status(item) for item in items] + #TODO(sirp): removing for glance + #items = [_translate_status(item) for item in items] return dict(images=items) def show(self, req, id): @@ -131,7 +132,12 @@ class Controller(wsgi.Controller): env = self._deserialize(req.body, req) instance_id = env["image"]["serverId"] name = env["image"]["name"] - return compute_api.ComputeAPI().snapshot(context, instance_id, name) + + image_meta = compute_api.ComputeAPI().snapshot( + context, instance_id, name) + + #TODO(sirp): need to map Glance attrs to OpenStackAPI attrs + return dict(image=image_meta) def update(self, req, id): # Users may not modify public images, and that's all that diff --git a/nova/compute/api.py b/nova/compute/api.py index 07c69bd31..5bb6fac91 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -263,10 +263,18 @@ class ComputeAPI(base.Base): """Snapshot the given instance.""" instance = self.db.instance_get_by_internal_id(context, instance_id) host = instance['host'] + + image_service = utils.import_object(FLAGS.image_service) + + data = {'name': name, 'is_public': True} + image_meta = image_service.create(context, data) rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), {"method": "snapshot_instance", - "args": {"instance_id": instance['id'], "name": name}}) + "args": {"instance_id": instance['id'], + "image_id": image_meta['id']}}) + + return image_meta def reboot(self, context, instance_id): """Reboot the given instance.""" diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6e8f34347..27e07ed59 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -225,7 +225,7 @@ class ComputeManager(manager.Manager): self._update_state(context, instance_id) @exception.wrap_exception - def snapshot_instance(self, context, instance_id, name): + def snapshot_instance(self, context, instance_id, image_id): """Snapshot an instance on this server.""" context = context.elevated() instance_ref = self.db.instance_get(context, instance_id) @@ -243,7 +243,7 @@ class ComputeManager(manager.Manager): instance_ref['state'], power_state.RUNNING) - self.driver.snapshot(instance_ref, name) + self.driver.snapshot(instance_ref, image_id) @exception.wrap_exception def rescue_instance(self, context, instance_id): diff --git a/nova/image/glance.py b/nova/image/glance.py index cc3192e7c..9575574d1 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -14,9 +14,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. - """Implementation of an image service that uses Glance as the backend""" +from __future__ import absolute_import import httplib import json import logging @@ -24,8 +24,6 @@ import urlparse import webob.exc -from nova.compute import api as compute_api -from nova import utils from nova import flags from nova import exception import nova.image.service @@ -33,165 +31,30 @@ import nova.image.service FLAGS = flags.FLAGS -flags.DEFINE_string('glance_teller_address', 'http://127.0.0.1', - 'IP address or URL where Glance\'s Teller service resides') -flags.DEFINE_string('glance_teller_port', '9191', - 'Port for Glance\'s Teller service') -flags.DEFINE_string('glance_parallax_address', 'http://127.0.0.1', - 'IP address or URL where Glance\'s Parallax service ' - 'resides') -flags.DEFINE_string('glance_parallax_port', '9292', - 'Port for Glance\'s Parallax service') - - -class TellerClient(object): - - def __init__(self): - self.address = FLAGS.glance_teller_address - self.port = FLAGS.glance_teller_port - url = urlparse.urlparse(self.address) - self.netloc = url.netloc - self.connection_type = {'http': httplib.HTTPConnection, - 'https': httplib.HTTPSConnection}[url.scheme] - - -class ParallaxClient(object): - - def __init__(self): - self.address = FLAGS.glance_parallax_address - self.port = FLAGS.glance_parallax_port - url = urlparse.urlparse(self.address) - self.netloc = url.netloc - self.connection_type = {'http': httplib.HTTPConnection, - 'https': httplib.HTTPSConnection}[url.scheme] - - def get_image_index(self): - """ - Returns a list of image id/name mappings from Parallax - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("GET", "images") - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(images=image_list) - data = json.loads(res.read())['images'] - return data - else: - logging.warn(_("Parallax returned HTTP error %d from " - "request for /images"), res.status_int) - return [] - finally: - c.close() - - def get_image_details(self): - """ - Returns a list of detailed image data mappings from Parallax - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("GET", "images/detail") - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(images=image_list) - data = json.loads(res.read())['images'] - return data - else: - logging.warn(_("Parallax returned HTTP error %d from " - "request for /images/detail"), res.status_int) - return [] - finally: - c.close() - - def get_image_metadata(self, image_id): - """ - Returns a mapping of image metadata from Parallax - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("GET", "images/%s" % image_id) - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(image=image_info) - data = json.loads(res.read())['image'] - return data - else: - # TODO(jaypipes): log the error? - return None - finally: - c.close() - - def add_image_metadata(self, image_metadata): - """ - Tells parallax about an image's metadata - """ - try: - c = self.connection_type(self.netloc, self.port) - body = json.dumps(image_metadata) - c.request("POST", "images", body) - res = c.getresponse() - if res.status == 200: - # Parallax returns a JSONified dict(image=image_info) - data = json.loads(res.read())['image'] - return data['id'] - else: - # TODO(jaypipes): log the error? - return None - finally: - c.close() - - def update_image_metadata(self, image_id, image_metadata): - """ - Updates Parallax's information about an image - """ - try: - c = self.connection_type(self.netloc, self.port) - body = json.dumps(image_metadata) - c.request("PUT", "images/%s" % image_id, body) - res = c.getresponse() - return res.status == 200 - finally: - c.close() - - def delete_image_metadata(self, image_id): - """ - Deletes Parallax's information about an image - """ - try: - c = self.connection_type(self.netloc, self.port) - c.request("DELETE", "images/%s" % image_id) - res = c.getresponse() - return res.status == 200 - finally: - c.close() - - class GlanceImageService(nova.image.service.BaseImageService): """Provides storage and retrieval of disk image objects within Glance.""" def __init__(self): - self.teller = TellerClient() - self.parallax = ParallaxClient() + from glance.client import Client #TODO(sirp): lazy-import glance + self.client = Client(FLAGS.glance_host, FLAGS.glance_port) def index(self, context): """ Calls out to Parallax for a list of images available """ - images = self.parallax.get_image_index() - return images + return self.client.get_images() def detail(self, context): """ Calls out to Parallax for a list of detailed image information """ - images = self.parallax.get_image_details() - return images + return self.client.get_images_detailed() def show(self, context, id): """ Returns a dict containing image data for the given opaque image id. """ - image = self.parallax.get_image_metadata(id) + image = self.client.get_image_meta(id) if image: return image raise exception.NotFound @@ -203,7 +66,7 @@ class GlanceImageService(nova.image.service.BaseImageService): :raises AlreadyExists if the image already exist. """ - return self.parallax.add_image_metadata(data) + return self.client.add_image(image_meta=data) def update(self, context, image_id, data): """Replace the contents of the given image with the new data. @@ -211,7 +74,7 @@ class GlanceImageService(nova.image.service.BaseImageService): :raises NotFound if the image does not exist. """ - self.parallax.update_image_metadata(image_id, data) + return self.client.update_image(image_id, data) def delete(self, context, image_id): """ @@ -220,10 +83,10 @@ class GlanceImageService(nova.image.service.BaseImageService): :raises NotFound if the image does not exist. """ - self.parallax.delete_image_metadata(image_id) + return self.client.delete_image(image_id) def delete_all(self): """ Clears out all images """ - pass + raise NotImplementedError diff --git a/nova/utils.py b/nova/utils.py index 15112faa2..8d3bf0a6b 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -304,6 +304,19 @@ class LazyPluggable(object): return getattr(backend, key) +class LoopingCallDone(Exception): + """The poll-function passed to LoopingCall can raise this exception to + break out of the loop normally. This is somewhat analogous to StopIteration. + + An optional return-value can be included as the argument to the exception; + this return-value will be returned by LoopingCall.wait() + """ + + def __init__(self, retvalue=True): + """:param retvalue: Value that LoopingCall.wait() should return""" + self.retvalue = retvalue + + class LoopingCall(object): def __init__(self, f=None, *args, **kw): self.args = args @@ -322,12 +335,15 @@ class LoopingCall(object): while self._running: self.f(*self.args, **self.kw) greenthread.sleep(interval) + except LoopingCallDone, e: + self.stop() + done.send(e.retvalue) except Exception: logging.exception('in looping call') done.send_exception(*sys.exc_info()) return - - done.send(True) + else: + done.send(True) self.done = done diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 00edfbdc8..18456be5a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -260,7 +260,7 @@ class LibvirtConnection(object): virt_dom.detachDevice(xml) @exception.wrap_exception - def snapshot(self, instance, name): + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ raise NotImplementedError( _("Instance snapshotting is not supported for libvirt" diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9d1b51848..308cf5834 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -237,15 +237,15 @@ class VMHelper(HelperBase): return template_vm_ref, [template_vdi_uuid, parent_uuid] @classmethod - def upload_image(cls, session, instance_id, vdi_uuids, image_name): + def upload_image(cls, session, instance_id, vdi_uuids, image_id): """ Requests that the Glance plugin bundle the specified VDIs and push them into Glance using the specified human-friendly name. """ - logging.debug(_("Asking xapi to upload %s as '%s'"), - vdi_uuids, image_name) + logging.debug(_("Asking xapi to upload %s as ID %s"), + vdi_uuids, image_id) params = {'vdi_uuids': vdi_uuids, - 'image_name': image_name, + 'image_id': image_id, 'glance_host': FLAGS.glance_host, 'glance_port': FLAGS.glance_port} @@ -427,9 +427,16 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, * parent_vhd snapshot """ - #TODO(sirp): we need to timeout this req after a while + max_attempts = FLAGS.xenapi_vhd_coalesce_max_attempts + attempts = {'counter': 0} def _poll_vhds(): + attempts['counter'] += 1 + if attempts['counter'] > max_attempts: + msg = (_("VHD coalesce attempts exceeded (%d > %d), giving up...") + % (attempts['counter'], max_attempts)) + raise exception.Error(msg) + scan_sr(session, instance_id, sr_ref) parent_uuid = get_vhd_parent_uuid(session, vdi_ref) if original_parent_uuid and (parent_uuid != original_parent_uuid): @@ -438,13 +445,12 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, "waiting for coalesce..."), parent_uuid, original_parent_uuid) else: - done.send(parent_uuid) + # Breakout of the loop (normally) and return the parent_uuid + raise utils.LoopingCallDone(parent_uuid) - done = event.Event() loop = utils.LoopingCall(_poll_vhds) loop.start(FLAGS.xenapi_vhd_coalesce_poll_interval, now=True) - parent_uuid = done.wait() - loop.stop() + parent_uuid = loop.wait() return parent_uuid diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 76f31635a..8f2fae08a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -120,11 +120,11 @@ class VMOps(object): timer.f = _wait_for_boot return timer.start(interval=0.5, now=True) - def snapshot(self, instance, name): + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance :param instance: instance to be snapshotted - :param name: name/label to be given to the snapshot + :param image_id: id of image to upload to Steps involved in a XenServer snapshot: @@ -160,7 +160,7 @@ class VMOps(object): try: # call plugin to ship snapshot off to glance VMHelper.upload_image( - self._session, instance.id, template_vdi_uuids, name) + self._session, instance.id, template_vdi_uuids, image_id) finally: self._destroy(instance, template_vm_ref, shutdown=False) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index f17c8f39d..d6be9f4a2 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -87,6 +87,10 @@ flags.DEFINE_float('xenapi_vhd_coalesce_poll_interval', 5.0, 'The interval used for polling of coalescing vhds.' ' Used only if connection_type=xenapi.') +flags.DEFINE_integer('xenapi_vhd_coalesce_max_attempts', + 5, + 'Max number of times to poll for VHD to coalesce.' + ' Used only if connection_type=xenapi.') flags.DEFINE_string('target_host', None, 'iSCSI Target Host') @@ -135,9 +139,9 @@ class XenAPIConnection(object): """Create VM instance""" self._vmops.spawn(instance) - def snapshot(self, instance, name): + def snapshot(self, instance, image_id): """ Create snapshot from a running VM instance """ - self._vmops.snapshot(instance, name) + self._vmops.snapshot(instance, image_id) def reboot(self, instance): """Reboot VM instance""" -- cgit From 8de96296dfb22d0e6c491fcaf072210dfbaa67e8 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 6 Jan 2011 23:38:01 -0600 Subject: Removing some FIXMEs --- nova/api/openstack/images.py | 15 ++++++++++----- nova/compute/api.py | 2 +- nova/image/glance.py | 6 ++++-- nova/utils.py | 3 ++- 4 files changed, 17 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 4d1af77d9..a826b8435 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -76,7 +76,14 @@ def _translate_status(item): 'decrypting': 'preparing', 'untarring': 'saving', 'available': 'active'} - item['status'] = status_mapping[item['status']] + try: + item['status'] = status_mapping[item['status']] + except KeyError: + # TODO(sirp): Performing translation of status (if necessary) here for + # now. Perhaps this should really be done in EC2 API and + # S3ImageService + pass + return item @@ -115,8 +122,7 @@ class Controller(wsgi.Controller): items = self._service.index(req.environ['nova.context']) items = common.limited(items, req) items = [_translate_keys(item) for item in items] - #TODO(sirp): removing for glance - #items = [_translate_status(item) for item in items] + items = [_translate_status(item) for item in items] return dict(images=items) def show(self, req, id): @@ -132,11 +138,10 @@ class Controller(wsgi.Controller): env = self._deserialize(req.body, req) instance_id = env["image"]["serverId"] name = env["image"]["name"] - + image_meta = compute_api.ComputeAPI().snapshot( context, instance_id, name) - #TODO(sirp): need to map Glance attrs to OpenStackAPI attrs return dict(image=image_meta) def update(self, req, id): diff --git a/nova/compute/api.py b/nova/compute/api.py index 5bb6fac91..75759744d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -263,7 +263,7 @@ class ComputeAPI(base.Base): """Snapshot the given instance.""" instance = self.db.instance_get_by_internal_id(context, instance_id) host = instance['host'] - + image_service = utils.import_object(FLAGS.image_service) data = {'name': name, 'is_public': True} diff --git a/nova/image/glance.py b/nova/image/glance.py index 9575574d1..be10f382b 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -26,17 +26,19 @@ import webob.exc from nova import flags from nova import exception +from nova import utils import nova.image.service FLAGS = flags.FLAGS +GlanceClient = utils.import_class('glance.client.Client') + class GlanceImageService(nova.image.service.BaseImageService): """Provides storage and retrieval of disk image objects within Glance.""" def __init__(self): - from glance.client import Client #TODO(sirp): lazy-import glance - self.client = Client(FLAGS.glance_host, FLAGS.glance_port) + self.client = GlanceClient(FLAGS.glance_host, FLAGS.glance_port) def index(self, context): """ diff --git a/nova/utils.py b/nova/utils.py index 8d3bf0a6b..cd53d31d5 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -306,7 +306,8 @@ class LazyPluggable(object): class LoopingCallDone(Exception): """The poll-function passed to LoopingCall can raise this exception to - break out of the loop normally. This is somewhat analogous to StopIteration. + break out of the loop normally. This is somewhat analogous to + StopIteration. An optional return-value can be included as the argument to the exception; this return-value will be returned by LoopingCall.wait() -- cgit From b8fc639af336630c56ce3807639a5e26c0d07982 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 7 Jan 2011 13:02:55 -0800 Subject: set the hostname factory in the service init --- nova/api/ec2/cloud.py | 7 ++++--- nova/compute/api.py | 22 +++++----------------- nova/tests/test_direct.py | 2 +- 3 files changed, 10 insertions(+), 21 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 8e3091a68..b16ab42bd 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -93,8 +93,10 @@ class CloudController(object): def __init__(self): self.network_manager = utils.import_object(FLAGS.network_manager) self.image_service = utils.import_object(FLAGS.image_service) - self.compute_api = compute_api.ComputeAPI(self.network_manager, - self.image_service) + self.compute_api = compute_api.ComputeAPI( + network_manager=self.network_manager, + image_service=self.image_service, + hostname_factory=internal_id_to_ec2_id) self.setup() def __str__(self): @@ -807,7 +809,6 @@ class CloudController(object): key_name=kwargs.get('key_name'), user_data=kwargs.get('user_data'), security_group=kwargs.get('security_group'), - hostname_format='ec2', availability_zone=kwargs.get('placement', {}).get( 'AvailabilityZone')) return self._format_run_instances(context, diff --git a/nova/compute/api.py b/nova/compute/api.py index 821fb06d5..591d99271 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -41,28 +41,18 @@ def id_to_default_hostname(internal_id): return str(internal_id) -def id_to_ec2_hostname(internal_id): - digits = [] - while internal_id != 0: - internal_id, remainder = divmod(internal_id, 36) - digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder]) - return "i-%s" % ''.join(reversed(digits)) - - -HOSTNAME_FORMATTERS = {'default': id_to_default_hostname, - 'ec2': id_to_ec2_hostname} - - class ComputeAPI(base.Base): """API for interacting with the compute manager.""" - def __init__(self, network_manager=None, image_service=None, **kwargs): + def __init__(self, network_manager=None, image_service=None, + hostname_factory=id_to_default_hostname, **kwargs): if not network_manager: network_manager = utils.import_object(FLAGS.network_manager) self.network_manager = network_manager if not image_service: image_service = utils.import_object(FLAGS.image_service) self.image_service = image_service + self.hostname_factory = hostname_factory super(ComputeAPI, self).__init__(**kwargs) def get_network_topic(self, context, instance_id): @@ -88,8 +78,7 @@ class ComputeAPI(base.Base): display_name='', description='', key_name=None, key_data=None, security_group='default', availability_zone=None, - user_data=None, - hostname_format='default'): + user_data=None): """Create the number of instances requested if quote and other arguments check out ok.""" @@ -160,7 +149,6 @@ class ComputeAPI(base.Base): elevated = context.elevated() instances = [] - generate_hostname = HOSTNAME_FORMATTERS[hostname_format] logging.debug(_("Going to run %s instances..."), num_instances) for num in range(num_instances): instance = dict(mac_address=utils.generate_mac(), @@ -179,7 +167,7 @@ class ComputeAPI(base.Base): security_group_id) # Set sane defaults if not specified - updates = dict(hostname=generate_hostname(internal_id)) + updates = dict(hostname=self.hostname_factory(internal_id)) if 'display_name' not in instance: updates['display_name'] = "Server %s" % internal_id diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py index d73c64ce0..78ad8ffed 100644 --- a/nova/tests/test_direct.py +++ b/nova/tests/test_direct.py @@ -72,7 +72,7 @@ class DirectTestCase(test.TestCase): resp_parsed = json.loads(resp.body) self.assertEqual(resp_parsed['data'], 'foo') - def test_req_params(self): + def test_post_params(self): req = webob.Request.blank('/fake/echo') req.environ['openstack.context'] = self.context req.method = 'POST' -- cgit From 8fe01c087943ca9b46d25c84d4408b752461e6bd Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 7 Jan 2011 16:05:06 -0800 Subject: some small cleanups --- nova/compute/api.py | 6 +----- nova/tests/test_direct.py | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 65e772d81..ffef20cee 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -253,11 +253,7 @@ class API(base.Base): def get(self, context, instance_id): """Get a single instance with the given ID.""" rv = self.db.instance_get_by_id(context, instance_id) - d = dict(rv.iteritems()) - # TODO(termie): this is ugly but required because the db layer returns - # models rather than dicts and the models support - # properties that are 'backreferences' - return d + return dict(rv.iteritems()) def get_all(self, context, project_id=None, reservation_id=None, fixed_ip=None): diff --git a/nova/tests/test_direct.py b/nova/tests/test_direct.py index 5c6ecd903..8a74b2296 100644 --- a/nova/tests/test_direct.py +++ b/nova/tests/test_direct.py @@ -101,7 +101,3 @@ class DirectCloudTestCase(test_cloud.CloudTestCase): def tearDown(self): super(DirectCloudTestCase, self).tearDown() direct.ROUTES = {} - -if __name__ == '__main__': - import unittest - unittest.main() -- cgit From d79600c1029ab91de8a81809df9efddc762351c0 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 7 Jan 2011 16:42:38 -0800 Subject: small cleanups --- nova/api/openstack/servers.py | 2 +- nova/db/sqlalchemy/models.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 0953b01ed..3f0fdc575 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -49,7 +49,7 @@ def _translate_detail_keys(inst): power_state.SHUTOFF: 'active', power_state.CRASHED: 'error'} inst_dict = {} - print inst + mapped_keys = dict(status='state', imageId='image_id', flavorId='instance_type', name='display_name', id='id') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 14cf7f617..d730a0ebc 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -90,7 +90,9 @@ class NovaBase(object): setattr(self, k, v) def iteritems(self): - """Make the model object behave like a dict""" + """Make the model object behave like a dict. + + Includes attributes from joins.""" local = dict(self) joined = dict([(k, v) for k, v in self.__dict__.iteritems() if not k[0] == '_']) -- cgit From 5afd9848ad09414c00062ceebdad45bca0604888 Mon Sep 17 00:00:00 2001 From: Muneyuki Noguchi Date: Tue, 11 Jan 2011 18:01:23 +0900 Subject: Add support for EBS volumes to the live migration feature. Currently, only AoE is supported. --- nova/api/ec2/cloud.py | 13 +- nova/compute/api.py | 3 +- nova/compute/manager.py | 55 +++--- nova/db/api.py | 5 + nova/db/sqlalchemy/api.py | 14 ++ nova/db/sqlalchemy/models.py | 1 - nova/livemigration_test/UT/computeManager.test.py | 12 +- nova/livemigration_test/UT/nova-manage.test.py | 3 + nova/network/manager.py | 1 - nova/scheduler/driver.py | 9 + nova/service.py | 2 +- nova/service.py.THIS | 222 ---------------------- nova/virt/libvirt_conn.py | 45 +++-- nova/volume/driver.py | 26 ++- nova/volume/manager.py | 12 +- 15 files changed, 137 insertions(+), 286 deletions(-) delete mode 100644 nova/service.py.THIS (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 3d88e5ef3..b8a553a92 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -679,22 +679,13 @@ class CloudController(object): ec2_id = None if (floating_ip_ref['fixed_ip'] and floating_ip_ref['fixed_ip']['instance']): - # modified by masumotok - internal_id = \ - floating_ip_ref['fixed_ip']['instance']['internal_id'] + internal_id = floating_ip_ref['fixed_ip']['instance'] ec2_id = internal_id_to_ec2_id(internal_id) address_rv = {'public_ip': address, 'instance_id': ec2_id} if context.user.is_admin(): - # modified by masumotok- b/c proj_id is never inserted - #details = "%s (%s)" % (address_rv['instance_id'], - # floating_ip_ref['project_id']) - if None != address_rv['instance_id']: - status = 'reserved' - else: - status = None details = "%s (%s)" % (address_rv['instance_id'], - status) + floating_ip_ref['project_id']) address_rv['instance_id'] = details addresses.append(address_rv) return {'addressesSet': addresses} diff --git a/nova/compute/api.py b/nova/compute/api.py index 5a776afa5..da41cc63c 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -233,7 +233,8 @@ class ComputeAPI(base.Base): terminated_at=datetime.datetime.utcnow()) host = instance['host'] - logging.error('terminate %s %s %s %s',context, FLAGS.compute_topic, host, self.db.queue_get_for(context, FLAGS.compute_topic, host)) + logging.error('terminate %s %s %s %s', context, FLAGS.compute_topic, + host, self.db.queue_get_for(context, FLAGS.compute_topic, host)) if host: rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1e8e11d04..a78789e63 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -122,7 +122,7 @@ class ComputeManager(manager.Manager): raise exception.Error(_("Instance has already been created")) self.db.instance_update(context, instance_id, - {'host': self.host, 'launched_on':self.host}) + {'host': self.host, 'launched_on': self.host}) self.db.instance_set_state(context, instance_id, @@ -443,21 +443,10 @@ class ComputeManager(manager.Manager): def pre_live_migration(self, context, instance_id, dest): """Any preparation for live migration at dst host.""" - # Getting volume info ( shlf/slot number ) + # Getting volume info instance_ref = db.instance_get(context, instance_id) ec2_id = instance_ref['hostname'] - volumes = [] - try: - volumes = db.volume_get_by_ec2_id(context, ec2_id) - except exception.NotFound: - logging.info(_('%s has no volume.'), ec2_id) - - shelf_slots = {} - for vol in volumes: - shelf, slot = db.volume_get_shelf_and_blade(context, vol['id']) - shelf_slots[vol.id] = (shelf, slot) - # Getting fixed ips fixed_ip = db.instance_get_fixed_address(context, instance_id) if None == fixed_ip: @@ -466,18 +455,22 @@ class ComputeManager(manager.Manager): tb = ''.join(traceback.format_tb(sys.exc_info()[2])) raise rpc.RemoteError(exc_type, val, tb) - # If any volume is mounted, prepare here. - if 0 != len(shelf_slots): - pass - - # Creating nova-instance-instance-xxx, this is written to libvirt.xml, - # and can be seen when executin "virsh nwfiter-list" On destination host, - # this nwfilter is necessary. - # In addition this method is creating security rule ingress rule onto - # destination host. + # if any volume is mounted, prepare here. + try: + for vol in db.volume_get_all_by_instance(context, instance_id): + self.volume_manager.setup_compute_volume(context, vol['id']) + except exception.NotFound: + logging.info(_("%s has no volume.") % ec2_id) + + # Creating nova-instance-instance-xxx, + # this is written to libvirt.xml, + # and can be seen when executin "virsh nwfiter-list" + # On destination host, this nwfilter is necessary. + # In addition this method is creating security rule ingress rule + # onto destination host. self.driver.setup_nwfilters_for_instance(instance_ref) - # 5. bridge settings + # bridge settings self.network_manager.setup_compute_network(context, instance_id) return True @@ -497,12 +490,23 @@ class ComputeManager(manager.Manager): "args": {'instance_id': instance_id, 'dest': dest}}) + instance_ref = db.instance_get(context, instance_id) + ec2_id = instance_ref['hostname'] if True != ret: logging.error(_('Pre live migration failed(err at %s)'), dest) db.instance_set_state(context, instance_id, power_state.RUNNING, 'running') + + try: + for vol in db.volume_get_all_by_instance(context, instance_id): + db.volume_update(context, + vol['id'], + {'status': 'in-use'}) + except exception.NotFound: + pass + return # Waiting for setting up nwfilter such as, nova-instance-instance-xxx. @@ -523,6 +527,11 @@ class ComputeManager(manager.Manager): logging.error(_('Timeout for pre_live_migration at %s'), dest) return + rpc.call(context, + FLAGS.volume_topic, + {"method": "check_for_export", + "args": {'instance_id': instance_id}}) + # Executing live migration # live_migration might raises ProcessExecution error, but # nothing must be recovered in this version. diff --git a/nova/db/api.py b/nova/db/api.py index cd35a2bd4..3004f5e6f 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -711,6 +711,11 @@ def volume_get_by_ec2_id(context, ec2_id): return IMPL.volume_get_by_ec2_id(context, ec2_id) +def volume_get_all_by_instance(context, instance_id): + """Get all volumes by instance id or raise if it does not exist.""" + return IMPL.volume_get_all_by_instance(context, instance_id) + + def volume_get_instance(context, volume_id): """Get the instance that a volume is attached to.""" return IMPL.volume_get_instance(context, volume_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b0d1ec1a7..04f60ccce 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -900,6 +900,8 @@ def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id): return _instance_get_sum_by_host_and_project(context, 'local_gb', hostname, proj_id) + + @require_context def instance_action_create(context, values): """Create an instance action from the values dictionary.""" @@ -1497,6 +1499,18 @@ def volume_get_by_ec2_id(context, ec2_id): return result +@require_admin_context +def volume_get_all_by_instance(context, instance_id): + session = get_session() + result = session.query(models.Volume).\ + filter_by(instance_id=instance_id).\ + filter_by(deleted=False).\ + all() + if not result: + raise exception.NotFound(_('No volume for instance %s') % instance_id) + return result + + @require_context def volume_ec2_id_exists(context, ec2_id, session=None): if not session: diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 994165ad1..1c1c23239 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -138,7 +138,6 @@ class NovaBase(object): # __tablename__ = 'hosts' # id = Column(String(255), primary_key=True) -# this class is created by masumotok class Host(BASE, NovaBase): """Represents a host where services are running""" __tablename__ = 'hosts' diff --git a/nova/livemigration_test/UT/computeManager.test.py b/nova/livemigration_test/UT/computeManager.test.py index 69ee876d1..829265733 100644 --- a/nova/livemigration_test/UT/computeManager.test.py +++ b/nova/livemigration_test/UT/computeManager.test.py @@ -170,7 +170,8 @@ class ComputeTestFunctions(unittest.TestCase): # mocks for pre_live_migration self.ctxt = context.get_admin_context() db.instance_get = Mock(return_value=self.instance1) - db.volume_get_by_ec2_id = Mock(return_value=[self.vol1, self.vol2]) + db.volume_get_all_by_instance \ + = Mock(return_value=[self.vol1, self.vol2]) db.volume_get_shelf_and_blade = Mock(return_value=(3, 4)) db.instance_get_fixed_address = Mock(return_value=self.fixed_ip1) db.security_group_get_by_instance \ @@ -199,7 +200,7 @@ class ComputeTestFunctions(unittest.TestCase): def test02(self): """02: NotAuthrized occurs on finding volume on DB. """ - db.volume_get_by_ec2_id \ + db.volume_get_all_by_instance \ = Mock(side_effect=exception.NotAuthorized('ERR')) self.assertRaises(exception.NotAuthorized, @@ -211,7 +212,7 @@ class ComputeTestFunctions(unittest.TestCase): def test03(self): """03: Unexpected exception occurs on finding volume on DB. """ - db.volume_get_by_ec2_id = Mock(side_effect=TypeError('ERR')) + db.volume_get_all_by_instance = Mock(side_effect=TypeError('ERR')) self.assertRaises(TypeError, self.manager.pre_live_migration, @@ -222,7 +223,6 @@ class ComputeTestFunctions(unittest.TestCase): def test04(self): """04: no volume and fixed ip found on DB, """ - db.volume_get_by_ec2_id = Mock(side_effect=exception.NotFound('ERR')) db.instance_get_fixed_address = Mock(return_value=None) self.assertRaises(rpc.RemoteError, @@ -230,10 +230,6 @@ class ComputeTestFunctions(unittest.TestCase): self.ctxt, 'dummy_ec2_id', 'host2') - - c1 = (0 <= sys.stderr.buffer.find('has no volume')) - - self.assertEqual(c1, True) def test05(self): """05: volume found and no fixed_ip found on DB. """ diff --git a/nova/livemigration_test/UT/nova-manage.test.py b/nova/livemigration_test/UT/nova-manage.test.py index 6db15cea0..257728386 100644 --- a/nova/livemigration_test/UT/nova-manage.test.py +++ b/nova/livemigration_test/UT/nova-manage.test.py @@ -97,6 +97,9 @@ class NovaManageTestFunctions(unittest.TestCase): # prepare test data self.setTestData() + # only AoE is supported for now + FLAGS.volume_driver = 'nova.volume.driver.AOEDriver' + def setTestData(self): import bin.novamanagetest diff --git a/nova/network/manager.py b/nova/network/manager.py index 5ebc52e77..13c9b2d8c 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -474,7 +474,6 @@ class VlanManager(NetworkManager): """Returns a fixed ip to the pool.""" self.db.fixed_ip_update(context, address, {'allocated': False}) - #def setup_compute_network(self, context, instance_id): def setup_compute_network(self, context, instance_id, network_ref=None): """Sets up matching network for compute hosts.""" if network_ref is None: diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py index 04061e38e..106c6ab7d 100644 --- a/nova/scheduler/driver.py +++ b/nova/scheduler/driver.py @@ -160,6 +160,15 @@ class Scheduler(object): power_state.PAUSED, 'migrating') + # Changing volume state + try: + for vol in db.volume_get_all_by_instance(context, instance_id): + db.volume_update(context, + vol['id'], + {'status': 'migrating'}) + except exception.NotFound: + pass + # Requesting live migration. return src diff --git a/nova/service.py b/nova/service.py index 04a60877e..7132a67b5 100644 --- a/nova/service.py +++ b/nova/service.py @@ -141,7 +141,7 @@ class Service(object): 'local_gb': local_gb, 'hypervisor_type': hypervisor, 'hypervisor_version': version, - 'cpu_info':cpu_xml }) + 'cpu_info': cpu_xml}) return host_ref def __getattr__(self, key): diff --git a/nova/service.py.THIS b/nova/service.py.THIS deleted file mode 100644 index 416448faa..000000000 --- a/nova/service.py.THIS +++ /dev/null @@ -1,222 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -""" -A service is a very thin wrapper around a Manager object. It exposes the -manager's public methods to other components of the system via rpc. It will -report state periodically to the database and is responsible for initiating -any periodic tasts that need to be executed on a given host. - -This module contains Service, a generic baseclass for all workers. -""" - -import inspect -import logging -import os - -from twisted.internet import defer -from twisted.internet import task -from twisted.application import service - -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova import rpc -from nova import utils - - -FLAGS = flags.FLAGS -flags.DEFINE_integer('report_interval', 10, - 'seconds between nodes reporting state to datastore', - lower_bound=1) - -flags.DEFINE_integer('periodic_interval', 60, - 'seconds between running periodic tasks', - lower_bound=1) - - -class Service(object, service.Service): - """Base class for workers that run on hosts.""" - - def __init__(self, host, binary, topic, manager, report_interval=None, - periodic_interval=None, *args, **kwargs): - self.host = host - self.binary = binary - self.topic = topic - self.manager_class_name = manager - self.report_interval = report_interval - self.periodic_interval = periodic_interval - super(Service, self).__init__(*args, **kwargs) - self.saved_args, self.saved_kwargs = args, kwargs - - def startService(self): # pylint: disable-msg C0103 - manager_class = utils.import_class(self.manager_class_name) - self.manager = manager_class(host=self.host, *self.saved_args, - **self.saved_kwargs) - self.manager.init_host() - self.model_disconnected = False - ctxt = context.get_admin_context() - - try: - host_ref = db.host_get_by_name(ctxt, self.host) - except exception.NotFound: - host_ref = db.host_create(ctxt, {'name': self.host}) - host_ref = self._update_host_ref(ctxt, host_ref) - - try: - service_ref = db.service_get_by_args(ctxt, - self.host, - self.binary) - self.service_id = service_ref['id'] - except exception.NotFound: - self._create_service_ref(ctxt) - - conn = rpc.Connection.instance() - if self.report_interval: - consumer_all = rpc.AdapterConsumer( - connection=conn, - topic=self.topic, - proxy=self) - consumer_node = rpc.AdapterConsumer( - connection=conn, - topic='%s.%s' % (self.topic, self.host), - proxy=self) - - consumer_all.attach_to_twisted() - consumer_node.attach_to_twisted() - - pulse = task.LoopingCall(self.report_state) - pulse.start(interval=self.report_interval, now=False) - - if self.periodic_interval: - pulse = task.LoopingCall(self.periodic_tasks) - pulse.start(interval=self.periodic_interval, now=False) - - def _create_service_ref(self, context): - service_ref = db.service_create(context, - {'host': self.host, - 'binary': self.binary, - 'topic': self.topic, - 'report_count': 0}) - self.service_id = service_ref['id'] - - def _update_host_ref(self, context, host_ref): - - if 0 <= self.manager_class_name.find('ComputeManager'): - vcpu = self.manager.driver.get_vcpu_number() - memory_mb = self.manager.get_memory_mb() - local_gb = self.manager.get_local_gb() - hypervisor = self.manager.driver.get_hypervisor_type() - version = self.manager.driver.get_hypervisor_version() - cpu_xml = self.manager.driver.get_cpu_xml() - - db.host_update(context, - host_ref['id'], - {'vcpus': vcpu, - 'memory_mb': memory_mb, - 'local_gb': local_gb, - 'hypervisor_type': hypervisor, - 'hypervisor_version': version, - 'cpu_info':cpu_xml }) - return host_ref - - def __getattr__(self, key): - manager = self.__dict__.get('manager', None) - return getattr(manager, key) - - @classmethod - def create(cls, - host=None, - binary=None, - topic=None, - manager=None, - report_interval=None, - periodic_interval=None): - """Instantiates class and passes back application object. - - Args: - host, defaults to FLAGS.host - binary, defaults to basename of executable - topic, defaults to bin_name - "nova-" part - manager, defaults to FLAGS._manager - report_interval, defaults to FLAGS.report_interval - periodic_interval, defaults to FLAGS.periodic_interval - """ - if not host: - host = FLAGS.host - if not binary: - binary = os.path.basename(inspect.stack()[-1][1]) - if not topic: - topic = binary.rpartition("nova-")[2] - if not manager: - manager = FLAGS.get('%s_manager' % topic, None) - if not report_interval: - report_interval = FLAGS.report_interval - if not periodic_interval: - periodic_interval = FLAGS.periodic_interval - logging.warn("Starting %s node", topic) - service_obj = cls(host, binary, topic, manager, - report_interval, periodic_interval) - - # This is the parent service that twistd will be looking for when it - # parses this file, return it so that we can get it into globals. - application = service.Application(binary) - service_obj.setServiceParent(application) - return application - - def kill(self): - """Destroy the service object in the datastore""" - try: - db.service_destroy(context.get_admin_context(), self.service_id) - except exception.NotFound: - logging.warn("Service killed that has no database entry") - - @defer.inlineCallbacks - def periodic_tasks(self): - """Tasks to be run at a periodic interval""" - yield self.manager.periodic_tasks(context.get_admin_context()) - - @defer.inlineCallbacks - def report_state(self): - """Update the state of this service in the datastore.""" - ctxt = context.get_admin_context() - try: - try: - service_ref = db.service_get(ctxt, self.service_id) - except exception.NotFound: - logging.debug("The service database object disappeared, " - "Recreating it.") - self._create_service_ref(ctxt) - service_ref = db.service_get(ctxt, self.service_id) - - db.service_update(ctxt, - self.service_id, - {'report_count': service_ref['report_count'] + 1}) - - # TODO(termie): make this pattern be more elegant. - if getattr(self, "model_disconnected", False): - self.model_disconnected = False - logging.error("Recovered model server connection!") - - # TODO(vish): this should probably only catch connection errors - except Exception: # pylint: disable-msg=W0702 - if not getattr(self, "model_disconnected", False): - self.model_disconnected = True - logging.exception("model server went away") - yield diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d1a53f275..044e6584c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -695,8 +695,9 @@ class LibvirtConnection(object): xmlstr = self._conn.getCapabilities() xml = libxml2.parseDoc(xmlstr) nodes = xml.xpathEval('//cpu') - if 1 != len(nodes): - msg = 'Unexpected xml format. tag "cpu" must be 1, but %d.' % len(nodes) + if 1 != len(nodes): + msg = 'Unexpected xml format. tag "cpu" must be 1, but %d.' \ + % len(nodes) msg += '\n' + xml.serialize() raise exception.Invalid(_(msg)) cpuxmlstr = re.sub("\n|[ ]+", ' ', nodes[0].serialize()) @@ -735,8 +736,8 @@ class LibvirtConnection(object): except libvirt.libvirtError: return False - def compareCPU(self, xml): - """ + def compareCPU(self, xml): + """ Check the host cpu is compatible to a cpu given by xml. "xml" must be a part of libvirt.openReadonly().getCapabilities(). return values follows by virCPUCompareResult. @@ -747,9 +748,9 @@ class LibvirtConnection(object): return self._conn.compareCPU(xml, 0) def live_migration(self, context, instance_ref, dest): - """ - Just spawning live_migration operation for - distributing high-load. + """ + Just spawning live_migration operation for + distributing high-load. """ greenthread.spawn(self._live_migration, context, instance_ref, dest) @@ -757,14 +758,21 @@ class LibvirtConnection(object): """ Do live migration.""" # Do live migration. - try: + try: uri = FLAGS.live_migration_uri % dest out, err = utils.execute("sudo virsh migrate --live %s %s" % (instance_ref.name, uri)) - except exception.ProcessExecutionError: + except exception.ProcessExecutionError: id = instance_ref['id'] db.instance_set_state(context, id, power_state.RUNNING, 'running') - raise + try: + for volume in db.volume_get_all_by_instance(context, id): + db.volume_update(context, + volume['id'], + {'status': 'in-use'}) + except exception.NotFound: + pass + raise exception.ProcessExecutionError # Waiting for completion of live_migration. timer = utils.LoopingCall(f=None) @@ -781,7 +789,7 @@ class LibvirtConnection(object): timer.start(interval=0.5, now=True) def _post_live_migration(self, context, instance_ref, dest): - """ + """ Post operations for live migration. Mainly, database updating. """ @@ -808,13 +816,14 @@ class LibvirtConnection(object): db.network_update(context, network_ref['id'], {'host': dest}) try: - floating_ip = db.instance_get_floating_address(context, instance_id) + floating_ip \ + = db.instance_get_floating_address(context, instance_id) # Not return if floating_ip is not found, otherwise, # instance never be accessible.. if None == floating_ip: logging.error('floating_ip is not found for %s ' % ec2_id) - else: - floating_ip_ref = db.floating_ip_get_by_address(context, + else: + floating_ip_ref = db.floating_ip_get_by_address(context, floating_ip) db.floating_ip_update(context, floating_ip_ref['address'], @@ -832,6 +841,14 @@ class LibvirtConnection(object): 'state': power_state.RUNNING, 'host': dest}) + try: + for volume in db.volume_get_all_by_instance(context, instance_id): + db.volume_update(context, + volume['id'], + {'status': 'in-use'}) + except exception.NotFound: + pass + logging.info(_('Live migrating %s to %s finishes successfully') % (ec2_id, dest)) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 8353b9712..aa40922e4 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -118,7 +118,7 @@ class VolumeDriver(object): """Removes an export for a logical volume.""" raise NotImplementedError() - def discover_volume(self, volume): + def discover_volume(self, context, volume): """Discover volume on a remote host.""" raise NotImplementedError() @@ -180,15 +180,35 @@ class AOEDriver(VolumeDriver): self._try_execute("sudo vblade-persist destroy %s %s" % (shelf_id, blade_id)) - def discover_volume(self, _volume): + def discover_volume(self, context, volume): """Discover volume on a remote host.""" self._execute("sudo aoe-discover") self._execute("sudo aoe-stat", check_exit_code=False) + shelf_id, blade_id = self.db.volume_get_shelf_and_blade(context, + volume['id']) + return "/dev/etherd/e%s.%s" % (shelf_id, blade_id) def undiscover_volume(self, _volume): """Undiscover volume on a remote host.""" pass + def check_for_export(self, context, volume_id): + """Make sure whether volume is exported.""" + (shelf_id, + blade_id) = self.db.volume_get_shelf_and_blade(context, + volume_id) + (out, _err) = self._execute("sudo vblade-persist ls --no-header") + exists = False + for line in out.split('\n'): + param = line.split(' ') + if len(param) == 6 and param[0] == str(shelf_id) \ + and param[1] == str(blade_id) and param[-1] == "run": + exists = True + break + if not exists: + logging.warning(_("vblade process for e%s.%s isn't running.") + % (shelf_id, blade_id)) + class FakeAOEDriver(AOEDriver): """Logs calls instead of executing.""" @@ -272,7 +292,7 @@ class ISCSIDriver(VolumeDriver): iscsi_portal = location.split(",")[0] return (iscsi_name, iscsi_portal) - def discover_volume(self, volume): + def discover_volume(self, _context, volume): """Discover volume on a remote host.""" iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'], volume['host']) diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 966334c50..03b757d81 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -137,7 +137,7 @@ class VolumeManager(manager.Manager): if volume_ref['host'] == self.host and FLAGS.use_local_volumes: path = self.driver.local_path(volume_ref) else: - path = self.driver.discover_volume(volume_ref) + path = self.driver.discover_volume(context, volume_ref) return path def remove_compute_volume(self, context, volume_id): @@ -148,3 +148,13 @@ class VolumeManager(manager.Manager): return True else: self.driver.undiscover_volume(volume_ref) + + def check_for_export(self, context, instance_id): + """Make sure whether volume is exported.""" + if FLAGS.volume_driver == 'nova.volume.driver.AOEDriver': + try: + for vol in self.db.volume_get_all_by_instance(context, + instance_id): + self.driver.check_for_export(context, vol['id']) + except exception.NotFound: + pass -- cgit From 69a2612be4e865063fa5982462673f1843e8befc Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Tue, 11 Jan 2011 13:54:23 -0600 Subject: Changes per Edays comments --- nova/api/openstack/images.py | 2 +- nova/compute/api.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 1102098b8..8f3f94ea1 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -138,7 +138,7 @@ class Controller(wsgi.Controller): instance_id = env["image"]["serverId"] name = env["image"]["name"] - image_meta = compute_api.ComputeAPI().snapshot( + image_meta = compute.ComputeAPI().snapshot( context, instance_id, name) return dict(image=image_meta) diff --git a/nova/compute/api.py b/nova/compute/api.py index 7e30d45d6..c02550419 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -333,10 +333,8 @@ class API(base.Base): instance = self.get(context, instance_id) host = instance['host'] - image_service = utils.import_object(FLAGS.image_service) - data = {'name': name, 'is_public': True} - image_meta = image_service.create(context, data) + image_meta = self.image_service.create(context, data) rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), {"method": "snapshot_instance", -- cgit From 32316a4ed6b4c175af8dd8ad3c345e54f161fd1c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 11 Jan 2011 17:07:24 -0800 Subject: use by-path instead of custom udev script --- nova/volume/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 477e0abf4..30d42af04 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -282,7 +282,7 @@ class ISCSIDriver(VolumeDriver): self._execute("sudo iscsiadm -m node -T %s -p %s --op update " "-n node.startup -v automatic" % (iscsi_name, iscsi_portal)) - return "/dev/iscsi/%s" % volume['name'] + return "/dev/by-path/ip-%s-iscsi-%s-lun-0" % (iscsi_portal, iscsi_name) def undiscover_volume(self, volume): """Undiscover volume on a remote host.""" -- cgit From 0cef0fe332859ce8b46d5d8d1745a63d377e1052 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 11 Jan 2011 17:21:39 -0800 Subject: more useful prefix and fix typo in string --- nova/volume/driver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 30d42af04..94e2aacf4 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -48,7 +48,7 @@ flags.DEFINE_integer('iscsi_num_targets', 'Number of iscsi target ids per host') flags.DEFINE_string('iscsi_target_prefix', 'iqn.2010-10.org.openstack:', 'prefix for iscsi volumes') -flags.DEFINE_string('iscsi_ip_prefix', '127.0', +flags.DEFINE_string('iscsi_ip_prefix', '$my_ip', 'discover volumes on the ip that starts with this prefix') @@ -282,7 +282,8 @@ class ISCSIDriver(VolumeDriver): self._execute("sudo iscsiadm -m node -T %s -p %s --op update " "-n node.startup -v automatic" % (iscsi_name, iscsi_portal)) - return "/dev/by-path/ip-%s-iscsi-%s-lun-0" % (iscsi_portal, iscsi_name) + return "/dev/disk/by-path/ip-%s-iscsi-%s-lun-0" % (iscsi_portal, + iscsi_name) def undiscover_volume(self, volume): """Undiscover volume on a remote host.""" -- cgit From eb146fd75183d80e50f8a67021dae565f4915b2e Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 12 Jan 2011 14:01:20 -0600 Subject: Fixing stub so tests pass --- nova/tests/api/openstack/fakes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 8315e85d9..d142c46e9 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -118,12 +118,12 @@ def stub_out_compute_api_snapshot(stubs): stubs.Set(nova.compute.API, 'snapshot', snapshot) -def stub_out_glance(stubs, initial_fixtures=[]): +def stub_out_glance(stubs, initial_fixtures=None): class FakeGlanceClient: def __init__(self, initial_fixtures): - self.fixtures = initial_fixtures + self.fixtures = initial_fixtures or [] def fake_get_images(self): return [dict(id=f['id'], name=f['name']) -- cgit From 204e5a2d9a481abba64ce31c12510d7e1bf288a6 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Wed, 12 Jan 2011 16:03:51 -0600 Subject: Adding TODO to clarify status --- nova/compute/api.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 63d0c59c1..923234e3a 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -337,6 +337,9 @@ class API(base.Base): instance = self.get(context, instance_id) host = instance['host'] + # TODO(sirp): When Glance supports images tied to servers, this should + # be replaced by something like 'is_public': False, 'server_id': + # instance_id data = {'name': name, 'is_public': True} image_meta = self.image_service.create(context, data) rpc.cast(context, -- cgit From 3419feff16e1974aa353188eee11609fc786148d Mon Sep 17 00:00:00 2001 From: termie Date: Wed, 12 Jan 2011 19:38:27 -0800 Subject: make sure get_all returns --- nova/compute/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index ffef20cee..630754395 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -261,8 +261,8 @@ class API(base.Base): given parameters. If there is no filter and the context is an admin, it will retreive all instances in the system.""" if reservation_id is not None: - rv = self.db.instance_get_all_by_reservation(context, - reservation_id) + return self.db.instance_get_all_by_reservation(context, + reservation_id) if fixed_ip is not None: return self.db.fixed_ip_get_instance(context, fixed_ip) if project_id or not context.is_admin: -- cgit From 2c7ffd2dab260c2bfc308ccd0c8d52e57a015413 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 13 Jan 2011 13:48:01 -0600 Subject: Fixing Image ID workaround and typo --- nova/api/openstack/images.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 4415db75f..9d56bc508 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -99,9 +99,11 @@ def _filter_keys(item, keys): def _convert_image_id_to_hash(image): - image_id = abs(hash(image['imageId'])) - image['imageId'] = image_id - image['id'] = image_id + if 'imageId' in image: + # Convert EC2-style ID (i-blah) to Rackspace-style (int) + image_id = abs(hash(image['imageId'])) + image['imageId'] = image_id + image['id'] = image_id class Controller(wsgi.Controller): @@ -155,7 +157,7 @@ class Controller(wsgi.Controller): instance_id = env["image"]["serverId"] name = env["image"]["name"] - image_meta = compute.ComputeAPI().snapshot( + image_meta = compute.API().snapshot( context, instance_id, name) return dict(image=image_meta) -- cgit From 98cb2518467374ae87d7dbc70890f79bb5084960 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 13 Jan 2011 14:01:21 -0600 Subject: Marking snapshots as private for now --- nova/compute/api.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 923234e3a..6d9d4fbbb 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -337,10 +337,7 @@ class API(base.Base): instance = self.get(context, instance_id) host = instance['host'] - # TODO(sirp): When Glance supports images tied to servers, this should - # be replaced by something like 'is_public': False, 'server_id': - # instance_id - data = {'name': name, 'is_public': True} + data = {'name': name, 'is_public': False} image_meta = self.image_service.create(context, data) rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), -- cgit From 0d6882fb2a3ec3b45b28120d00b8b4ff5fbc9187 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Thu, 13 Jan 2011 17:08:23 -0600 Subject: Fix for Pep-8 --- nova/image/glance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/image/glance.py b/nova/image/glance.py index 2f3a031fa..593c4bce6 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -92,4 +92,4 @@ class GlanceImageService(service.BaseImageService): """ Clears out all images """ - pass #raise NotImplementedError + pass -- cgit From c57ccba743c54786e28317194000bcf22dc5b69e Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Fri, 14 Jan 2011 08:26:25 +0900 Subject: checking based on pep8 --- nova/compute/manager.py | 10 ++++++---- nova/scheduler/driver.py | 6 +++--- nova/scheduler/manager.py | 2 -- nova/virt/libvirt_conn.py | 36 +++++++++++++++++------------------- 4 files changed, 26 insertions(+), 28 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 00de85828..5db756362 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -561,7 +561,7 @@ class ComputeManager(manager.Manager): self.network_manager.setup_compute_network(context, instance_id) # Creating filters to hypervisors and firewalls. - # An example is that nova-instance-instance-xxx, + # An example is that nova-instance-instance-xxx, # which is written to libvirt.xml( check "virsh nwfilter-list ) # On destination host, this nwfilter is necessary. # In addition, this method is creating filtering rule @@ -575,8 +575,8 @@ class ComputeManager(manager.Manager): instance_ref = db.instance_get(context, instance_id) ec2_id = instance_ref['hostname'] - try: - # Checking volume node is working correctly when any volumes + try: + # Checking volume node is working correctly when any volumes # are attached to instances. rpc.call(context, FLAGS.volume_topic, @@ -584,7 +584,9 @@ class ComputeManager(manager.Manager): "args": {'instance_id': instance_id}}) # Asking dest host to preparing live migration. - compute_topic = db.queue_get_for(context, FLAGS.compute_topic, dest) + compute_topic = db.queue_get_for(context, + FLAGS.compute_topic, + dest) rpc.call(context, compute_topic, diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py index d44a3ae44..699462b12 100644 --- a/nova/scheduler/driver.py +++ b/nova/scheduler/driver.py @@ -130,16 +130,16 @@ class Scheduler(object): msg = _('Unexpected err: not found cpu_info for %s on DB.hosts') raise exception.Invalid(msg % orighost) - try : + try: rpc.call(context, db.queue_get_for(context, FLAGS.compute_topic, dest), {"method": 'compare_cpu', "args": {'xml': cpuinfo}}) - except rpc.RemoteError, e: + except rpc.RemoteError, e: msg = '%s doesnt have compatibility to %s(where %s launching at)\n' msg += 'result:%s \n' - logging.error( _(msg) % (dest, src, ec2_id, ret)) + logging.error(_(msg) % (dest, src, ec2_id, ret)) raise e # Checking dst host still has enough capacities. diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 308fcffa2..b6627453d 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -69,7 +69,6 @@ class SchedulerManager(manager.Manager): "args": kwargs}) LOG.debug(_("Casting to %s %s for %s"), topic, host, method) - # NOTE (masumotok) : This method should be moved to nova.api.ec2.admin. # Based on bear design summit discussion, # just put this here for bexar release. @@ -112,4 +111,3 @@ class SchedulerManager(manager.Manager): 'local_gb': hdd} return {'ret': True, 'phy_resource': h_resource, 'usage': u_resource} - diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 3024515b8..f3f837153 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -96,7 +96,7 @@ flags.DEFINE_string('live_migration_uri', flags.DEFINE_string('live_migration_flag', "VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER", 'Define live migration behavior.') -flags.DEFINE_integer('live_migration_bandwidth', 0, +flags.DEFINE_integer('live_migration_bandwidth', 0, 'Define live migration behavior') flags.DEFINE_string('live_migration_timeout_sec', 10, 'Timeout second for pre_live_migration is completed.') @@ -817,7 +817,6 @@ class LibvirtConnection(object): def refresh_security_group_members(self, security_group_id): self.firewall_driver.refresh_security_group_members(security_group_id) - def compare_cpu(self, xml): """ Check the host cpu is compatible to a cpu given by xml. @@ -827,9 +826,8 @@ class LibvirtConnection(object): 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult' """ - ret = self._conn.compareCPU(xml, 0) - if ret <= 0 : + if ret <= 0: url = 'http://libvirt.org/html/libvirt-libvirt.html' url += '#virCPUCompareResult\n' msg = 'CPU does not have compativility.\n' @@ -837,22 +835,22 @@ class LibvirtConnection(object): msg += 'Refer to %s' msg = _(msg) raise exception.Invalid(msg % (ret, url)) - return + return def ensure_filtering_rules_for_instance(self, instance_ref): - """ Setting up inevitable filtering rules on compute node, - and waiting for its completion. + """ Setting up inevitable filtering rules on compute node, + and waiting for its completion. To migrate an instance, filtering rules to hypervisors and firewalls are inevitable on destination host. - ( Waiting only for filterling rules to hypervisor, + ( Waiting only for filterling rules to hypervisor, since filtering rules to firewall rules can be set faster). Concretely, the below method must be called. - setup_basic_filtering (for nova-basic, etc.) - prepare_instance_filter(for nova-instance-instance-xxx, etc.) - + to_xml may have to be called since it defines PROJNET, PROJMASK. - but libvirt migrates those value through migrateToURI(), + but libvirt migrates those value through migrateToURI(), so , no need to be called. Don't use thread for this method since migration should @@ -879,7 +877,7 @@ class LibvirtConnection(object): msg = _('Timeout migrating for %s(%s)') raise exception.Error(msg % (ec2_id, instance_ref.name)) time.sleep(0.5) - + def live_migration(self, context, instance_ref, dest): """ Just spawning live_migration operation for @@ -895,21 +893,21 @@ class LibvirtConnection(object): duri = FLAGS.live_migration_uri % dest flaglist = FLAGS.live_migration_flag.split(',') - flagvals = [ getattr(libvirt, x.strip()) for x in flaglist ] - logical_sum = reduce(lambda x,y: x|y, flagvals) + flagvals = [getattr(libvirt, x.strip()) for x in flaglist] + logical_sum = reduce(lambda x, y: x | y, flagvals) bandwidth = FLAGS.live_migration_bandwidth - - if self.read_only: + + if self.read_only: tmpconn = self._connect(self.libvirt_uri, False) dom = tmpconn.lookupByName(instance_ref.name) dom.migrateToURI(duri, logical_sum, None, bandwidth) tmpconn.close() - else : + else: dom = self._conn.lookupByName(instance_ref.name) dom.migrateToURI(duri, logical_sum, None, bandwidth) - - except Exception, e: + + except Exception, e: id = instance_ref['id'] db.instance_set_state(context, id, power_state.RUNNING, 'running') try: @@ -950,7 +948,7 @@ class LibvirtConnection(object): # Releasing security group ingress rule. if FLAGS.firewall_driver == \ 'nova.virt.libvirt_conn.IptablesFirewallDriver': - try : + try: self.firewall_driver.remove_instance(instance_ref) except KeyError, e: pass -- cgit From fa5024b384953d30e91117a0c2874560e086aa58 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Fri, 14 Jan 2011 08:55:56 +0900 Subject: remove ">>>MERGE" iin nova/db/sqlalchemy/api.py --- nova/db/sqlalchemy/api.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b38a08a83..4ea85a094 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2117,4 +2117,3 @@ def console_get(context, console_id, instance_id=None): {'instance': idesc, 'console_id': console_id}) return result ->>>>>>> MERGE-SOURCE -- cgit From 500b268d0ef83b4770f9883690564e458cf94247 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 13 Jan 2011 18:57:29 -0500 Subject: pep8. Someday I'll remember 2 blank lines between module methods. --- nova/wsgi.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/wsgi.py b/nova/wsgi.py index 817cd0478..b4cca9138 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -393,9 +393,10 @@ class Serializer(object): result.appendChild(node) return result + def paste_config_file(basename): """Find the best location in the system for a paste config file. - + Search Order ------------ @@ -424,6 +425,7 @@ def paste_config_file(basename): if os.path.exists(configfile): return configfile + def load_paste_configuration(filename, appname): """Returns a paste configuration dict, or None.""" filename = os.path.abspath(filename) @@ -434,6 +436,7 @@ def load_paste_configuration(filename, appname): pass return config + def load_paste_app(filename, appname): """Builds a wsgi app from a paste config, None if app not configured.""" filename = os.path.abspath(filename) @@ -444,8 +447,9 @@ def load_paste_app(filename, appname): pass return app + def paste_config_to_flags(config, mixins): - for k,v in mixins.iteritems(): + for k, v in mixins.iteritems(): value = config.get(k, v) converted_value = FLAGS[k].parser.Parse(value) setattr(FLAGS, k, converted_value) -- cgit From cf0e5bd3eeb6b175b53df6ae0a0ef8957ec7ba13 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 14 Jan 2011 11:24:45 -0600 Subject: Create and use a generic handler for RPC calls --- nova/compute/api.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index 90273da36..40b9e33e8 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -331,7 +331,7 @@ class API(base.Base): return self.db.instance_get_all(context) def _cast_compute_message(self, method, context, instance_id, host=None): - """Generic handler for RPC calls to compute.""" + """Generic handler for RPC casts to compute.""" if not host: instance = self.get(context, instance_id) host = instance['host'] @@ -339,6 +339,15 @@ class API(base.Base): kwargs = {'method': method, 'args': {'instance_id': instance_id}} rpc.cast(context, queue, kwargs) + def _call_compute_message(self, method, context, instance_id, host=None): + """Generic handler for RPC calls to compute.""" + if not host: + instance = self.get(context, instance_id) + host = instance["host"] + queue = self.db.queue_get_for(context, FLAGS.compute_topic, host) + kwargs = {"method": method, "args": {"instance_id": instance_id}} + return rpc.call(context, queue, kwargs) + def snapshot(self, context, instance_id, name): """Snapshot the given instance.""" self._cast_compute_message('snapshot_instance', context, instance_id) @@ -357,7 +366,10 @@ class API(base.Base): def get_diagnostics(self, context, instance_id): """Retrieve diagnostics for the given instance.""" - self._cast_compute_message('get_diagnostics', context, instance_id) + return self._call_compute_message( + "get_diagnostics", + context, + instance_id) def get_actions(self, context, instance_id): """Retrieve actions for the given instance.""" -- cgit From 76e875476848ee7f4aa483f65484903115e2bb49 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 10:25:44 -0800 Subject: import re, remove extra call in cloud.py. Move get_console_output to compute_api --- nova/api/ec2/cloud.py | 17 +---------------- nova/compute/api.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 25 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 630aaeaf2..4adcb7415 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -130,15 +130,6 @@ class CloudController(object): result[key] = [line] return result - def _trigger_refresh_security_group(self, context, security_group): - nodes = set([instance['host'] for instance in security_group.instances - if instance['host'] is not None]) - for node in nodes: - rpc.cast(context, - '%s.%s' % (FLAGS.compute_topic, node), - {"method": "refresh_security_group", - "args": {"security_group_id": security_group.id}}) - def _get_availability_zone_by_host(self, context, host): services = db.service_get_all_by_host(context, host) if len(services) > 0: @@ -522,13 +513,7 @@ class CloudController(object): # instance_id is passed in as a list of instances ec2_id = instance_id[0] instance_id = ec2_id_to_id(ec2_id) - instance_ref = self.compute_api.get(context, instance_id) - output = rpc.call(context, - '%s.%s' % (FLAGS.compute_topic, - instance_ref['host']), - {"method": "get_console_output", - "args": {"instance_id": instance_ref['id']}}) - + output = self.compute_api.get_console_output(context, instance_id) now = datetime.datetime.utcnow() return {"InstanceId": ec2_id, "Timestamp": now, diff --git a/nova/compute/api.py b/nova/compute/api.py index 90273da36..e6cfa459e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -21,6 +21,7 @@ Handles all requests relating to instances (guest vms). """ import datetime +import re import time from nova import db @@ -385,23 +386,23 @@ class API(base.Base): def get_ajax_console(self, context, instance_id): """Get a url to an AJAX Console""" - instance = self.get(context, instance_id) - - output = rpc.call(context, - '%s.%s' % (FLAGS.compute_topic, - instance['host']), - {'method': 'get_ajax_console', - 'args': {'instance_id': instance['id']}}) - + output = self._call_compute_message('get_ajax_console', + context, + instance_id) rpc.cast(context, '%s' % FLAGS.ajax_console_proxy_topic, {'method': 'authorize_ajax_console', 'args': {'token': output['token'], 'host': output['host'], 'port': output['port']}}) - return {'url': '%s?token=%s' % (FLAGS.ajax_console_proxy_url, output['token'])} + def get_console_output(self, context, instance_id): + """Get console output for an an instance""" + return self._call_compute_message('get_console_output', + context, + instance_id) + def lock(self, context, instance_id): """lock the instance with instance_id""" self._cast_compute_message('lock_instance', context, instance_id) -- cgit From e0dcd52b98de4bfe9843b148decf22526713dea2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:00:47 -0800 Subject: remove TrialTestCase again and fix merge issues --- nova/test.py | 99 +------------------------------------------ nova/tests/test_api.py | 4 +- nova/tests/test_log.py | 8 ++-- nova/tests/test_middleware.py | 2 +- nova/tests/test_twistd.py | 2 +- 5 files changed, 10 insertions(+), 105 deletions(-) (limited to 'nova') diff --git a/nova/test.py b/nova/test.py index 5922e4b1c..a26b85b04 100644 --- a/nova/test.py +++ b/nova/test.py @@ -23,8 +23,6 @@ and some black magic for inline callbacks. """ import datetime -import sys -import time import unittest import mox @@ -38,7 +36,6 @@ from nova import fakerabbit from nova import flags from nova import rpc from nova.network import manager as network_manager -from nova.tests import fake_flags FLAGS = flags.FLAGS @@ -74,7 +71,8 @@ class TestCase(unittest.TestCase): FLAGS.fixed_range, 5, 16, FLAGS.vlan_start, - FLAGS.vpn_start) + FLAGS.vpn_start, + FLAGS.fixed_range_v6) # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators @@ -139,96 +137,3 @@ class TestCase(unittest.TestCase): _wrapped.func_name = self.originalAttach.func_name rpc.Consumer.attach_to_eventlet = _wrapped - - -class TrialTestCase(trial_unittest.TestCase): - """Test case base class for all unit tests""" - def setUp(self): - """Run before each test method to initialize test environment""" - super(TrialTestCase, self).setUp() - # NOTE(vish): We need a better method for creating fixtures for tests - # now that we have some required db setup for the system - # to work properly. - self.start = datetime.datetime.utcnow() - ctxt = context.get_admin_context() - if db.network_count(ctxt) != 5: - network_manager.VlanManager().create_networks(ctxt, - FLAGS.fixed_range, - 5, 16, - FLAGS.vlan_start, - FLAGS.vpn_start, - FLAGS.fixed_range_v6) - - # emulate some of the mox stuff, we can't use the metaclass - # because it screws with our generators - self.mox = mox.Mox() - self.stubs = stubout.StubOutForTesting() - self.flag_overrides = {} - self.injected = [] - self._original_flags = FLAGS.FlagValuesDict() - - def tearDown(self): - """Runs after each test method to finalize/tear down test - environment.""" - try: - self.mox.UnsetStubs() - self.stubs.UnsetAll() - self.stubs.SmartUnsetAll() - self.mox.VerifyAll() - # NOTE(vish): Clean up any ips associated during the test. - ctxt = context.get_admin_context() - db.fixed_ip_disassociate_all_by_timeout(ctxt, FLAGS.host, - self.start) - db.network_disassociate_all(ctxt) - for x in self.injected: - try: - x.stop() - except AssertionError: - pass - - if FLAGS.fake_rabbit: - fakerabbit.reset_all() - - db.security_group_destroy_all(ctxt) - super(TrialTestCase, self).tearDown() - finally: - self.reset_flags() - - def flags(self, **kw): - """Override flag variables for a test""" - for k, v in kw.iteritems(): - if k in self.flag_overrides: - self.reset_flags() - raise Exception( - 'trying to override already overriden flag: %s' % k) - self.flag_overrides[k] = getattr(FLAGS, k) - setattr(FLAGS, k, v) - - def reset_flags(self): - """Resets all flag variables for the test. Runs after each test""" - FLAGS.Reset() - for k, v in self._original_flags.iteritems(): - setattr(FLAGS, k, v) - - def run(self, result=None): - test_method = getattr(self, self._testMethodName) - setattr(self, - self._testMethodName, - self._maybeInlineCallbacks(test_method, result)) - rv = super(TrialTestCase, self).run(result) - setattr(self, self._testMethodName, test_method) - return rv - - def _maybeInlineCallbacks(self, func, result): - def _wrapped(): - g = func() - if isinstance(g, defer.Deferred): - return g - if not hasattr(g, 'send'): - return defer.succeed(g) - - inlined = defer.inlineCallbacks(func) - d = inlined() - return d - _wrapped.func_name = func.func_name - return _wrapped diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index d22d7beb1..17789c25c 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -79,7 +79,7 @@ class FakeHttplibConnection(object): pass -class XmlConversionTestCase(test.TrialTestCase): +class XmlConversionTestCase(test.TestCase): """Unit test api xml conversion""" def test_number_conversion(self): conv = apirequest._try_convert @@ -96,7 +96,7 @@ class XmlConversionTestCase(test.TrialTestCase): self.assertEqual(conv('-0'), 0) -class ApiEc2TestCase(test.TrialTestCase): +class ApiEc2TestCase(test.TestCase): """Unit test for the cloud controller on an EC2 API""" def setUp(self): super(ApiEc2TestCase, self).setUp() diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py index beb1d97cf..868a5ead3 100644 --- a/nova/tests/test_log.py +++ b/nova/tests/test_log.py @@ -9,7 +9,7 @@ def _fake_context(): return context.RequestContext(1, 1) -class RootLoggerTestCase(test.TrialTestCase): +class RootLoggerTestCase(test.TestCase): def setUp(self): super(RootLoggerTestCase, self).setUp() self.log = log.logging.root @@ -46,7 +46,7 @@ class RootLoggerTestCase(test.TrialTestCase): self.assert_(True) # didn't raise exception -class NovaFormatterTestCase(test.TrialTestCase): +class NovaFormatterTestCase(test.TestCase): def setUp(self): super(NovaFormatterTestCase, self).setUp() self.flags(logging_context_format_string="HAS CONTEXT "\ @@ -78,7 +78,7 @@ class NovaFormatterTestCase(test.TrialTestCase): self.assertEqual("NOCTXT: baz --DBG\n", self.stream.getvalue()) -class NovaLoggerTestCase(test.TrialTestCase): +class NovaLoggerTestCase(test.TestCase): def setUp(self): super(NovaLoggerTestCase, self).setUp() self.flags(default_log_levels=["nova-test=AUDIT"], verbose=False) @@ -96,7 +96,7 @@ class NovaLoggerTestCase(test.TrialTestCase): self.assertEqual(log.AUDIT, l.level) -class VerboseLoggerTestCase(test.TrialTestCase): +class VerboseLoggerTestCase(test.TestCase): def setUp(self): super(VerboseLoggerTestCase, self).setUp() self.flags(default_log_levels=["nova.test=AUDIT"], verbose=True) diff --git a/nova/tests/test_middleware.py b/nova/tests/test_middleware.py index 0febf52d6..9d49167ba 100644 --- a/nova/tests/test_middleware.py +++ b/nova/tests/test_middleware.py @@ -38,7 +38,7 @@ def conditional_forbid(req): return 'OK' -class LockoutTestCase(test.TrialTestCase): +class LockoutTestCase(test.TestCase): """Test case for the Lockout middleware.""" def setUp(self): # pylint: disable-msg=C0103 super(LockoutTestCase, self).setUp() diff --git a/nova/tests/test_twistd.py b/nova/tests/test_twistd.py index 75007b9c8..ff8627c3b 100644 --- a/nova/tests/test_twistd.py +++ b/nova/tests/test_twistd.py @@ -28,7 +28,7 @@ from nova import test FLAGS = flags.FLAGS -class TwistdTestCase(test.TrialTestCase): +class TwistdTestCase(test.TestCase): def setUp(self): super(TwistdTestCase, self).setUp() self.Options = twistd.WrapTwistedOptions(twistd.TwistdServerOptions) -- cgit From f16030423d43272c5c3bea7fe51a0e03f0d95846 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:19:51 -0800 Subject: undo accidental removal of fake_flags --- nova/test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/test.py b/nova/test.py index a26b85b04..881baccd5 100644 --- a/nova/test.py +++ b/nova/test.py @@ -27,8 +27,6 @@ import unittest import mox import stubout -from twisted.internet import defer -from twisted.trial import unittest as trial_unittest from nova import context from nova import db @@ -36,6 +34,7 @@ from nova import fakerabbit from nova import flags from nova import rpc from nova.network import manager as network_manager +from nova.tests import fake_flags FLAGS = flags.FLAGS -- cgit From bf0d75e6f78bc3c66dce8481d44e52c40a9addb0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:20:46 -0800 Subject: fix bad function signature in create_networks --- nova/network/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 4d553f074..a92bd3f99 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -239,8 +239,8 @@ class NetworkManager(manager.Manager): """Get the network host for the current context.""" raise NotImplementedError() - def create_networks(self, context, num_networks, network_size, cidr_v6, - *args, **kwargs): + def create_networks(self, context, cidr, num_networks, network_size, + cidr_v6, *args, **kwargs): """Create networks based on parameters.""" raise NotImplementedError() -- cgit From d0713a6a2149274eeeef6fd22e7da4706a8190ec Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 14 Jan 2011 11:36:48 -0800 Subject: removed rpc in cloud --- nova/api/ec2/cloud.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4adcb7415..fb7e6a59f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -37,7 +37,6 @@ from nova import exception from nova import flags from nova import log as logging from nova import network -from nova import rpc from nova import utils from nova import volume from nova.compute import instance_types -- cgit From 4f920a8316afc4becdabbc0a75959a8e8017836f Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 14 Jan 2011 17:43:34 -0800 Subject: remove print statement --- nova/tests/api/openstack/test_servers.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index bb598ddeb..0396daf98 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -101,7 +101,6 @@ class ServersTest(unittest.TestCase): def test_get_server_by_id(self): req = webob.Request.blank('/v1.0/servers/1') res = req.get_response(nova.api.API('os')) - print res.body res_dict = json.loads(res.body) self.assertEqual(res_dict['server']['id'], '1') self.assertEqual(res_dict['server']['name'], 'server1') -- cgit From 731126b299da757588656fa72b291ca4da96b5fe Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 14 Jan 2011 17:44:47 -0800 Subject: pep8 --- nova/db/sqlalchemy/models.py | 2 +- nova/tests/test_console.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 0e56ea7e6..e8fb1f439 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -91,7 +91,7 @@ class NovaBase(object): def iteritems(self): """Make the model object behave like a dict. - + Includes attributes from joins.""" local = dict(self) joined = dict([(k, v) for k, v in self.__dict__.iteritems() diff --git a/nova/tests/test_console.py b/nova/tests/test_console.py index 69e2109e6..85bf94458 100644 --- a/nova/tests/test_console.py +++ b/nova/tests/test_console.py @@ -130,4 +130,3 @@ class ConsoleTestCase(test.TestCase): self.context, console_id) db.instance_destroy(self.context, instance_id) - -- cgit From 69c11c27c20c74aced491ecfe78a80872ad6232a Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Fri, 14 Jan 2011 17:54:36 -0800 Subject: pep8 fixes... largely to things from trunk? --- nova/api/ec2/cloud.py | 3 ++- nova/db/sqlalchemy/api.py | 6 +++--- nova/network/manager.py | 6 ++---- nova/virt/libvirt_conn.py | 3 +-- 4 files changed, 8 insertions(+), 10 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index d1aec23be..a1ae70fb0 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -515,7 +515,8 @@ class CloudController(object): # instance_id is passed in as a list of instances ec2_id = instance_id[0] instance_id = ec2_id_to_id(ec2_id) - output = self.compute_api.get_console_output(context, instance_id=instance_id) + output = self.compute_api.get_console_output( + context, instance_id=instance_id) now = datetime.datetime.utcnow() return {"InstanceId": ec2_id, "Timestamp": now, diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 3ba69af9e..b63b84bed 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -611,9 +611,9 @@ def fixed_ip_get_instance_v6(context, address): session = get_session() mac = utils.to_mac(address) - result = session.query(models.Instance - ).filter_by(mac_address=mac - ).first() + result = session.query(models.Instance).\ + filter_by(mac_address=mac).\ + first() return result diff --git a/nova/network/manager.py b/nova/network/manager.py index a92bd3f99..2a043cc6b 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -517,10 +517,8 @@ class VlanManager(NetworkManager): net['vlan'] = vlan net['bridge'] = 'br%s' % vlan if(FLAGS.use_ipv6): - cidr_v6 = "%s/%s" % ( - fixed_net_v6[start_v6], - significant_bits_v6 - ) + cidr_v6 = "%s/%s" % (fixed_net_v6[start_v6], + significant_bits_v6) net['cidr_v6'] = cidr_v6 # NOTE(vish): This makes ports unique accross the cloud, a more diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 073a8e5bb..b06246135 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -995,8 +995,7 @@ class NWFilterFirewall(FirewallDriver): ['no-mac-spoofing', 'no-ip-spoofing', 'no-arp-spoofing', - 'allow-dhcp-server' - ])) + 'allow-dhcp-server'])) self._define_filter(self.nova_base_ipv4_filter) self._define_filter(self.nova_base_ipv6_filter) self._define_filter(self.nova_dhcp_filter) -- cgit From 525544e689334346305ecc11552105fc1b32a5dd Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Sun, 16 Jan 2011 14:54:35 +0900 Subject: merged to rev 561 and fixed based on reviewer's comment --- nova/compute/manager.py | 65 +++++++++++----- nova/db/api.py | 30 -------- nova/db/sqlalchemy/api.py | 111 +++++---------------------- nova/db/sqlalchemy/models.py | 40 +++++----- nova/network/api.py | 1 + nova/scheduler/driver.py | 173 +++++++++++++++++++++++++++---------------- nova/scheduler/manager.py | 25 ++++--- nova/service.py | 29 +------- nova/virt/fake.py | 31 ++++++++ nova/virt/libvirt_conn.py | 86 ++++++++++++++++----- nova/virt/xenapi_conn.py | 30 ++++++++ nova/volume/manager.py | 9 +-- 12 files changed, 349 insertions(+), 281 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 5db756362..9c8cb363c 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -39,6 +39,7 @@ import logging import socket import functools +from nova import context from nova import db from nova import exception from nova import flags @@ -117,6 +118,37 @@ class ComputeManager(manager.Manager): """ self.driver.init_host() + + def update_service(self, ctxt, host, binary): + """Insert compute node specific information to DB.""" + + try: + service_ref = db.service_get_by_args(ctxt, + host, + binary) + except exception.NotFound: + msg = _(("""Cannot insert compute manager specific info""" + """Because no service record found.""")) + raise exception.invalid(msg) + + # Updating host information + vcpu = self.driver.get_vcpu_number() + memory_mb = self.driver.get_memory_mb() + local_gb = self.driver.get_local_gb() + hypervisor = self.driver.get_hypervisor_type() + version = self.driver.get_hypervisor_version() + cpu_info = self.driver.get_cpu_info() + + db.service_update(ctxt, + service_ref['id'], + {'vcpus': vcpu, + 'memory_mb': memory_mb, + 'local_gb': local_gb, + 'hypervisor_type': hypervisor, + 'hypervisor_version': version, + 'cpu_info': cpu_info}) + + def _update_state(self, context, instance_id): """Update the state of an instance from the driver info.""" # FIXME(ja): include other fields from state? @@ -530,9 +562,9 @@ class ComputeManager(manager.Manager): self.db.volume_detached(context, volume_id) return True - def compare_cpu(self, context, xml): + def compare_cpu(self, context, cpu_info): """ Check the host cpu is compatible to a cpu given by xml.""" - return self.driver.compare_cpu(xml) + return self.driver.compare_cpu(cpu_info) def pre_live_migration(self, context, instance_id, dest): """Any preparation for live migration at dst host.""" @@ -548,11 +580,11 @@ class ComputeManager(manager.Manager): raise exception.NotFound(msg) # If any volume is mounted, prepare here. - try: - for vol in db.volume_get_all_by_instance(context, instance_id): - self.volume_manager.setup_compute_volume(context, vol['id']) - except exception.NotFound: + if len(instance_ref['volumes']) == 0: logging.info(_("%s has no volume.") % ec2_id) + else: + for v in instance_ref['volumes']: + self.volume_manager.setup_compute_volume(context, v['id']) # Bridge settings # call this method prior to ensure_filtering_rules_for_instance, @@ -578,16 +610,16 @@ class ComputeManager(manager.Manager): try: # Checking volume node is working correctly when any volumes # are attached to instances. - rpc.call(context, - FLAGS.volume_topic, - {"method": "check_for_export", - "args": {'instance_id': instance_id}}) + if len(instance_ref['volumes']) != 0: + rpc.call(context, + FLAGS.volume_topic, + {"method": "check_for_export", + "args": {'instance_id': instance_id}}) # Asking dest host to preparing live migration. compute_topic = db.queue_get_for(context, FLAGS.compute_topic, dest) - rpc.call(context, compute_topic, {"method": "pre_live_migration", @@ -602,13 +634,10 @@ class ComputeManager(manager.Manager): power_state.RUNNING, 'running') - try: - for vol in db.volume_get_all_by_instance(context, instance_id): - db.volume_update(context, - vol['id'], - {'status': 'in-use'}) - except exception.NotFound: - pass + for v in instance_ref['volumes']: + db.volume_update(context, + v['id'], + {'status': 'in-use'}) # e should be raised. just calling "raise" may raise NotFound. raise e diff --git a/nova/db/api.py b/nova/db/api.py index aee5d1bb7..8c1e0d54d 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -730,11 +730,6 @@ def volume_get_by_ec2_id(context, ec2_id): return IMPL.volume_get_by_ec2_id(context, ec2_id) -def volume_get_all_by_instance(context, instance_id): - """Get all volumes by instance id or raise if it does not exist.""" - return IMPL.volume_get_all_by_instance(context, instance_id) - - def volume_get_instance(context, volume_id): """Get the instance that a volume is attached to.""" return IMPL.volume_get_instance(context, volume_id) @@ -952,31 +947,6 @@ def host_get_networks(context, host): return IMPL.host_get_networks(context, host) -def host_create(context, value): - """Create a host from the values dictionary.""" - return IMPL.host_create(context, value) - - -def host_get(context, host_id): - """Get an host or raise if it does not exist.""" - return IMPL.host_get(context, host_id) - - -def host_get_all(context, session=None): - """Get all hosts or raise if it does not exist.""" - return IMPL.host_get_all(context) - - -def host_get_by_name(context, host): - """Get an host or raise if it does not exist.""" - return IMPL.host_get_by_name(context, host) - - -def host_update(context, host, values): - """Set the given properties on an host and update it.""" - return IMPL.host_update(context, host, values) - - ################## diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 4ea85a094..9843b7edb 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -864,10 +864,10 @@ def instance_get_all_by_host(context, hostname): if not session: session = get_session() - result = session.query(models.Instance - ).filter_by(host=hostname - ).filter_by(deleted=can_read_deleted(context) - ).all() + result = session.query(models.Instance).\ + filter_by(host=hostname).\ + filter_by(deleted=can_read_deleted(context)).\ + all() if not result: return [] return result @@ -877,11 +877,11 @@ def instance_get_all_by_host(context, hostname): def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id): session = get_session() - result = session.query(models.Instance - ).filter_by(host=hostname - ).filter_by(project_id=proj_id - ).filter_by(deleted=can_read_deleted(context) - ).value(column) + result = session.query(models.Instance).\ + filter_by(host=hostname).\ + filter_by(project_id=proj_id).\ + filter_by(deleted=can_read_deleted(context)).\ + value(column) if not result: return 0 return result @@ -889,20 +889,26 @@ def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id): @require_context def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id): - return _instance_get_sum_by_host_and_project(context, 'vcpus', hostname, + return _instance_get_sum_by_host_and_project(context, + 'vcpus', + hostname, proj_id) @require_context def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id): - return _instance_get_sum_by_host_and_project(context, 'memory_mb', - hostname, proj_id) + return _instance_get_sum_by_host_and_project(context, + 'memory_mb', + hostname, + proj_id) @require_context def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id): - return _instance_get_sum_by_host_and_project(context, 'local_gb', - hostname, proj_id) + return _instance_get_sum_by_host_and_project(context, + 'local_gb', + hostname, + proj_id) @require_context @@ -1470,18 +1476,6 @@ def volume_get_all_by_project(context, project_id): all() -@require_admin_context -def volume_get_all_by_instance(context, instance_id): - session = get_session() - result = session.query(models.Volume).\ - filter_by(instance_id=instance_id).\ - filter_by(deleted=False).\ - all() - if not result: - raise exception.NotFound(_('No volume for instance %s') % instance_id) - return result - - @require_admin_context def volume_get_instance(context, volume_id): session = get_session() @@ -1946,71 +1940,6 @@ def host_get_networks(context, host): all() -@require_admin_context -def host_create(context, values): - host_ref = models.Host() - for (key, value) in values.iteritems(): - host_ref[key] = value - host_ref.save() - return host_ref - - -@require_admin_context -def host_get(context, host_id, session=None): - if not session: - session = get_session() - - result = session.query(models.Host - ).filter_by(deleted=False - ).filter_by(id=host_id - ).first() - - if not result: - raise exception.NotFound('No host for id %s' % host_id) - - return result - - -@require_admin_context -def host_get_all(context, session=None): - if not session: - session = get_session() - - result = session.query(models.Host - ).filter_by(deleted=False - ).all() - - if not result: - raise exception.NotFound('No host record found .') - - return result - - -@require_admin_context -def host_get_by_name(context, host, session=None): - if not session: - session = get_session() - - result = session.query(models.Host - ).filter_by(deleted=False - ).filter_by(name=host - ).first() - - if not result: - raise exception.NotFound('No host for name %s' % host) - - return result - - -@require_admin_context -def host_update(context, host_id, values): - session = get_session() - with session.begin(): - host_ref = host_get(context, host_id, session=session) - for (key, value) in values.iteritems(): - host_ref[key] = value - - ################## diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 61f8b3cc9..add37fe19 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -138,38 +138,38 @@ class NovaBase(object): # __tablename__ = 'hosts' # id = Column(String(255), primary_key=True) -class Host(BASE, NovaBase): - """Represents a host where services are running""" - __tablename__ = 'hosts' - id = Column(Integer, primary_key=True) - name = Column(String(255)) - vcpus = Column(Integer, nullable=False, default=-1) - memory_mb = Column(Integer, nullable=False, default=-1) - local_gb = Column(Integer, nullable=False, default=-1) - hypervisor_type = Column(String(128)) - hypervisor_version = Column(Integer, nullable=False, default=-1) - cpu_info = Column(String(1024)) - deleted = Column(Boolean, default=False) - # C: when calling service_create() - # D: never deleted. instead of deleting cloumn "deleted" is true - # when host is down - # b/c Host.id is foreign key of service, and records - # of the "service" table are not deleted. - # R: Column "deleted" is true when calling hosts_up() and host is down. - class Service(BASE, NovaBase): """Represents a running service on a host.""" __tablename__ = 'services' id = Column(Integer, primary_key=True) - host = Column(String(255)) # , ForeignKey('hosts.id')) + #host_id = Column(Integer, ForeignKey('hosts.id'), nullable=True) + #host = relationship(Host, backref=backref('services')) + host = Column(String(255)) binary = Column(String(255)) topic = Column(String(255)) report_count = Column(Integer, nullable=False, default=0) disabled = Column(Boolean, default=False) availability_zone = Column(String(255), default='nova') + # The below items are compute node only. + # -1 or None is inserted for other service. + vcpus = Column(Integer, nullable=False, default=-1) + memory_mb = Column(Integer, nullable=False, default=-1) + local_gb = Column(Integer, nullable=False, default=-1) + hypervisor_type = Column(String(128)) + hypervisor_version = Column(Integer, nullable=False, default=-1) + # Note(masumotok): Expected Strings example: + # + # '{"arch":"x86_64", "model":"Nehalem", + # "topology":{"sockets":1, "threads":2, "cores":3}, + # features:[ "tdtscp", "xtpr"]}' + # + # Points are "json translatable" and it must have all + # dictionary keys above. + cpu_info = Column(String(512)) + class Certificate(BASE, NovaBase): """Represents a an x509 certificate""" diff --git a/nova/network/api.py b/nova/network/api.py index 09d20b57e..bf43acb51 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -67,6 +67,7 @@ class API(base.Base): floating_ip = self.db.floating_ip_get_by_address(context, floating_ip) # NOTE(vish): Perhaps we should just pass this on to compute and # let compute communicate with network. + host = fixed_ip['network']['host'] rpc.cast(context, self.db.queue_get_for(context, FLAGS.network_topic, host), {"method": "associate_floating_ip", diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py index 699462b12..4ab1e2fbf 100644 --- a/nova/scheduler/driver.py +++ b/nova/scheduler/driver.py @@ -75,110 +75,159 @@ class Scheduler(object): instance_ref = db.instance_get(context, instance_id) ec2_id = instance_ref['hostname'] - # Checking instance state. + # Checking instance. + self._live_migration_src_check(context, instance_ref) + + # Checking destination host. + self._live_migration_dest_check(context, instance_ref, dest) + + # Common checking. + self._live_migration_common_check(context, instance_ref, dest) + + # Changing instance_state. + db.instance_set_state(context, + instance_id, + power_state.PAUSED, + 'migrating') + + # Changing volume state + for v in instance_ref['volumes']: + db.volume_update(context, + v['id'], + {'status': 'migrating'}) + + # Return value is necessary to send request to src + # Check _schedule() in detail. + src = instance_ref['host'] + return src + + def _live_migration_src_check(self, context, instance_ref): + """Live migration check routine (for src host)""" + + # Checking instance is running. if power_state.RUNNING != instance_ref['state'] or \ 'running' != instance_ref['state_description']: msg = _('Instance(%s) is not running') + ec2_id = instance_ref['hostname'] raise exception.Invalid(msg % ec2_id) - # Checking destination host exists - dhost_ref = db.host_get_by_name(context, dest) + # Checing volume node is running when any volumes are mounted to the instance. + if len(instance_ref['volumes']) != 0: + services = db.service_get_all_by_topic(context, 'volume') + if len(services) < 1 or not self.service_is_up(services[0]): + msg = _('volume node is not alive(time synchronize problem?)') + raise exception.Invalid(msg) - # Checking whether The host where instance is running - # and dest is not same. + # Checking src host is alive. src = instance_ref['host'] - if dest == src: - msg = _('%s is where %s is running now. choose other host.') - raise exception.Invalid(msg % (dest, ec2_id)) - - # Checking dest is compute node. services = db.service_get_all_by_topic(context, 'compute') - if dest not in [service.host for service in services]: + services = [service for service in services if service.host == src] + if len(services) < 1 or not self.service_is_up(services[0]): + msg = _('%s is not alive(time synchronize problem?)') + raise exception.Invalid(msg % src) + + + def _live_migration_dest_check(self, context, instance_ref, dest): + """Live migration check routine (for destination host)""" + + # Checking dest exists and compute node. + dservice_refs = db.service_get_all_by_host(context, dest) + if len(dservice_refs) <= 0 : + msg = _('%s does not exists.') + raise exception.Invalid(msg % dest) + + dservice_ref = dservice_refs[0] + if dservice_ref['topic'] != 'compute': msg = _('%s must be compute node') raise exception.Invalid(msg % dest) # Checking dest host is alive. - service = [service for service in services if service.host == dest] - service = service[0] - if not self.service_is_up(service): + if not self.service_is_up(dservice_ref): msg = _('%s is not alive(time synchronize problem?)') raise exception.Invalid(msg % dest) - # NOTE(masumotok): Below pre-checkings are followed by - # http://wiki.libvirt.org/page/TodoPreMigrationChecks + # Checking whether The host where instance is running + # and dest is not same. + src = instance_ref['host'] + if dest == src: + ec2_id = instance_ref['hostname'] + msg = _('%s is where %s is running now. choose other host.') + raise exception.Invalid(msg % (dest, ec2_id)) + + # Checking dst host still has enough capacities. + self.has_enough_resource(context, instance_ref, dest) - # Checking hypervisor is same. + def _live_migration_common_check(self, context, instance_ref, dest): + """ + Live migration check routine. + Below pre-checkings are followed by + http://wiki.libvirt.org/page/TodoPreMigrationChecks + + """ + + # Checking dest exists. + dservice_refs = db.service_get_all_by_host(context, dest) + if len(dservice_refs) <= 0 : + msg = _('%s does not exists.') + raise exception.Invalid(msg % dest) + dservice_ref = dservice_refs[0] + + # Checking original host( where instance was launched at) exists. orighost = instance_ref['launched_on'] - ohost_ref = db.host_get_by_name(context, orighost) + oservice_refs = db.service_get_all_by_host(context, orighost) + if len(oservice_refs) <= 0 : + msg = _('%s(where instance was launched at) does not exists.') + raise exception.Invalid(msg % orighost) + oservice_ref = oservice_refs[0] - otype = ohost_ref['hypervisor_type'] - dtype = dhost_ref['hypervisor_type'] + # Checking hypervisor is same. + otype = oservice_ref['hypervisor_type'] + dtype = dservice_ref['hypervisor_type'] if otype != dtype: msg = _('Different hypervisor type(%s->%s)') raise exception.Invalid(msg % (otype, dtype)) # Checkng hypervisor version. - oversion = ohost_ref['hypervisor_version'] - dversion = dhost_ref['hypervisor_version'] + oversion = oservice_ref['hypervisor_version'] + dversion = dservice_ref['hypervisor_version'] if oversion > dversion: msg = _('Older hypervisor version(%s->%s)') raise exception.Invalid(msg % (oversion, dversion)) # Checking cpuinfo. - cpuinfo = ohost_ref['cpu_info'] - if str != type(cpuinfo): - msg = _('Unexpected err: not found cpu_info for %s on DB.hosts') - raise exception.Invalid(msg % orighost) - + cpu_info = oservice_ref['cpu_info'] try: rpc.call(context, - db.queue_get_for(context, FLAGS.compute_topic, dest), - {"method": 'compare_cpu', - "args": {'xml': cpuinfo}}) + db.queue_get_for(context, FLAGS.compute_topic, dest), + {"method": 'compare_cpu', + "args": {'cpu_info': cpu_info}}) except rpc.RemoteError, e: - msg = '%s doesnt have compatibility to %s(where %s launching at)\n' - msg += 'result:%s \n' - logging.error(_(msg) % (dest, src, ec2_id, ret)) + msg = _('%s doesnt have compatibility to %s(where %s launching at)') + ec2_id = instance_ref['hostname'] + src = instance_ref['host'] + logging.error(msg % (dest, src, ec2_id)) raise e - # Checking dst host still has enough capacities. - self.has_enough_resource(context, instance_id, dest) - - # Changing instance_state. - db.instance_set_state(context, - instance_id, - power_state.PAUSED, - 'migrating') - - # Changing volume state - try: - for vol in db.volume_get_all_by_instance(context, instance_id): - db.volume_update(context, - vol['id'], - {'status': 'migrating'}) - except exception.NotFound: - pass - - # Return value is necessary to send request to src - # Check _schedule() in detail. - return src - - def has_enough_resource(self, context, instance_id, dest): + def has_enough_resource(self, context, instance_ref, dest): """ Check if destination host has enough resource for live migration""" # Getting instance information - instance_ref = db.instance_get(context, instance_id) ec2_id = instance_ref['hostname'] vcpus = instance_ref['vcpus'] mem = instance_ref['memory_mb'] hdd = instance_ref['local_gb'] # Gettin host information - host_ref = db.host_get_by_name(context, dest) - total_cpu = int(host_ref['vcpus']) - total_mem = int(host_ref['memory_mb']) - total_hdd = int(host_ref['local_gb']) + service_refs = db.service_get_all_by_host(context, dest) + if len(service_refs) <= 0 : + msg = _('%s does not exists.') + raise exception.Invalid(msg % dest) + service_ref = service_refs[0] + + total_cpu = int(service_ref['vcpus']) + total_mem = int(service_ref['memory_mb']) + total_hdd = int(service_ref['local_gb']) instances_ref = db.instance_get_all_by_host(context, dest) for i_ref in instances_ref: @@ -196,4 +245,4 @@ class Scheduler(object): msg = '%s doesnt have enough resource for %s' % (dest, ec2_id) raise exception.NotEmpty(msg) - logging.debug(_('%s has enough resource for %s') % (dest, ec2_id)) + logging.debug(_('%s has_enough_resource() for %s') % (dest, ec2_id)) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index b6627453d..56ffbf221 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -76,20 +76,27 @@ class SchedulerManager(manager.Manager): """ show the physical/usage resource given by hosts.""" try: - host_ref = db.host_get_by_name(context, host) + services = db.service_get_all_by_host(context, host) except exception.NotFound: return {'ret': False, 'msg': 'No such Host'} except: raise + compute = [ s for s in services if s['topic'] == 'compute'] + if 0 == len(compute): + service_ref = services[0] + else: + service_ref = compute[0] + # Getting physical resource information - h_resource = {'vcpus': host_ref['vcpus'], - 'memory_mb': host_ref['memory_mb'], - 'local_gb': host_ref['local_gb']} + h_resource = {'vcpus': service_ref['vcpus'], + 'memory_mb': service_ref['memory_mb'], + 'local_gb': service_ref['local_gb']} # Getting usage resource information u_resource = {} - instances_ref = db.instance_get_all_by_host(context, host_ref['name']) + instances_ref = db.instance_get_all_by_host(context, + service_ref['host']) if 0 == len(instances_ref): return {'ret': True, 'phy_resource': h_resource, 'usage': {}} @@ -98,11 +105,11 @@ class SchedulerManager(manager.Manager): project_ids = list(set(project_ids)) for p_id in project_ids: vcpus = db.instance_get_vcpu_sum_by_host_and_project(context, - host, - p_id) + host, + p_id) mem = db.instance_get_memory_sum_by_host_and_project(context, - host, - p_id) + host, + p_id) hdd = db.instance_get_disk_sum_by_host_and_project(context, host, p_id) diff --git a/nova/service.py b/nova/service.py index ff44e49a8..7323c7ff1 100644 --- a/nova/service.py +++ b/nova/service.py @@ -81,12 +81,6 @@ class Service(object): self.model_disconnected = False ctxt = context.get_admin_context() - try: - host_ref = db.host_get_by_name(ctxt, self.host) - except exception.NotFound: - host_ref = db.host_create(ctxt, {'name': self.host}) - host_ref = self._update_host_ref(ctxt, host_ref) - try: service_ref = db.service_get_by_args(ctxt, self.host, @@ -95,6 +89,9 @@ class Service(object): except exception.NotFound: self._create_service_ref(ctxt) + if 'nova-compute' == self.binary: + self.manager.update_service(ctxt, self.host, self.binary) + conn1 = rpc.Connection.instance(new=True) conn2 = rpc.Connection.instance(new=True) if self.report_interval: @@ -129,26 +126,6 @@ class Service(object): 'availability_zone': zone}) self.service_id = service_ref['id'] - def _update_host_ref(self, context, host_ref): - - if 0 <= self.manager_class_name.find('ComputeManager'): - vcpu = self.manager.driver.get_vcpu_number() - memory_mb = self.manager.driver.get_memory_mb() - local_gb = self.manager.driver.get_local_gb() - hypervisor = self.manager.driver.get_hypervisor_type() - version = self.manager.driver.get_hypervisor_version() - cpu_xml = self.manager.driver.get_cpu_xml() - - db.host_update(context, - host_ref['id'], - {'vcpus': vcpu, - 'memory_mb': memory_mb, - 'local_gb': local_gb, - 'hypervisor_type': hypervisor, - 'hypervisor_version': version, - 'cpu_info': cpu_xml}) - return host_ref - def __getattr__(self, key): manager = self.__dict__.get('manager', None) return getattr(manager, key) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 9186d885e..3b53f714f 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -297,6 +297,37 @@ class FakeConnection(object): 'username': 'fakeuser', 'password': 'fakepassword'} + def get_cpu_info(self): + """This method is supported only libvirt. """ + return + + def get_vcpu_number(self): + """This method is supported only libvirt. """ + return -1 + + def get_memory_mb(self): + """This method is supported only libvirt..""" + return -1 + + def get_local_gb(self): + """This method is supported only libvirt..""" + return -1 + + def get_hypervisor_type(self): + """This method is supported only libvirt..""" + return + + def get_hypervisor_version(self): + """This method is supported only libvirt..""" + return -1 + + def compare_cpu(self, xml): + """This method is supported only libvirt..""" + raise NotImplementedError('This method is supported only libvirt.') + + def live_migration(self, context, instance_ref, dest): + """This method is supported only libvirt..""" + raise NotImplementedError('This method is supported only libvirt.') class FakeInstance(object): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f3f837153..93e768ae9 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -36,6 +36,7 @@ Supports KVM, QEMU, UML, and XEN. """ +import json import os import shutil import re @@ -82,6 +83,9 @@ flags.DEFINE_string('injected_network_template', flags.DEFINE_string('libvirt_xml_template', utils.abspath('virt/libvirt.xml.template'), 'Libvirt XML Template') +flags.DEFINE_string('cpuinfo_xml_template', + utils.abspath('virt/cpuinfo.xml.template'), + 'CpuInfo XML Template (used only live migration now)') flags.DEFINE_string('libvirt_type', 'kvm', 'Libvirt domain type (valid options are: ' @@ -110,6 +114,11 @@ flags.DEFINE_string('firewall_driver', 'nova.virt.libvirt_conn.IptablesFirewallDriver', 'Firewall driver (defaults to iptables)') +class cpuinfo: + arch = '' + vendor = '' + def __init__(self): pass + def get_connection(read_only): # These are loaded late so that there's no need to install these @@ -145,6 +154,7 @@ class LibvirtConnection(object): self.libvirt_uri = self.get_uri() self.libvirt_xml = open(FLAGS.libvirt_xml_template).read() + self.cpuinfo_xml = open(FLAGS.cpuinfo_xml_template).read() self._wrapped_conn = None self.read_only = read_only @@ -774,7 +784,7 @@ class LibvirtConnection(object): """ Get hypervisor version """ return self._conn.getVersion() - def get_cpu_xml(self): + def get_cpu_info(self): """ Get cpuinfo information """ xmlstr = self._conn.getCapabilities() xml = libxml2.parseDoc(xmlstr) @@ -784,8 +794,40 @@ class LibvirtConnection(object): % len(nodes) msg += '\n' + xml.serialize() raise exception.Invalid(_(msg)) - cpuxmlstr = re.sub("\n|[ ]+", ' ', nodes[0].serialize()) - return cpuxmlstr + + arch = xml.xpathEval('//cpu/arch')[0].getContent() + model = xml.xpathEval('//cpu/model')[0].getContent() + vendor = xml.xpathEval('//cpu/vendor')[0].getContent() + + topology_node = xml.xpathEval('//cpu/topology')[0].get_properties() + topology = dict() + while topology_node != None: + name = topology_node.get_name() + topology[name] = topology_node.getContent() + topology_node = topology_node.get_next() + + keys = ['cores', 'sockets', 'threads'] + tkeys = topology.keys() + if list(set(tkeys)) != list(set(keys)): + msg = _('Invalid xml: topology(%s) must have %s') + raise exception.Invalid(msg % (str(topology), ', '.join(keys))) + + feature_nodes = xml.xpathEval('//cpu/feature') + features = list() + for nodes in feature_nodes: + feature_name = nodes.get_properties().getContent() + features.append(feature_name) + + template = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """ + """"topology":{"cores":"%s", "threads":"%s", "sockets":"%s"}, """ + """"features":[%s]}""") + c = topology['cores'] + s = topology['sockets'] + t = topology['threads'] + f = [ '"%s"' % x for x in features] + cpu_info = template % (arch, model, vendor, c, s, t, ', '.join(f)) + return cpu_info + def block_stats(self, instance_name, disk): """ @@ -817,7 +859,7 @@ class LibvirtConnection(object): def refresh_security_group_members(self, security_group_id): self.firewall_driver.refresh_security_group_members(security_group_id) - def compare_cpu(self, xml): + def compare_cpu(self, cpu_info): """ Check the host cpu is compatible to a cpu given by xml. "xml" must be a part of libvirt.openReadonly().getCapabilities(). @@ -826,6 +868,11 @@ class LibvirtConnection(object): 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult' """ + dic = json.loads(cpu_info) + print dic + xml = str(Template(self.cpuinfo_xml, searchList=dic)) + msg = _('Checking cpu_info: instance was launched this cpu.\n: %s ') + LOG.info(msg % xml) ret = self._conn.compareCPU(xml, 0) if ret <= 0: url = 'http://libvirt.org/html/libvirt-libvirt.html' @@ -910,13 +957,10 @@ class LibvirtConnection(object): except Exception, e: id = instance_ref['id'] db.instance_set_state(context, id, power_state.RUNNING, 'running') - try: - for volume in db.volume_get_all_by_instance(context, id): - db.volume_update(context, - volume['id'], - {'status': 'in-use'}) - except exception.NotFound: - pass + for v in instance_ref['volumes']: + db.volume_update(context, + v['id'], + {'status': 'in-use'}) raise e @@ -939,6 +983,7 @@ class LibvirtConnection(object): Post operations for live migration. Mainly, database updating. """ + LOG.info('post livemigration operation is started..') # Detaching volumes. # (not necessary in current version ) @@ -949,7 +994,7 @@ class LibvirtConnection(object): if FLAGS.firewall_driver == \ 'nova.virt.libvirt_conn.IptablesFirewallDriver': try: - self.firewall_driver.remove_instance(instance_ref) + self.firewall_driver.unfilter_instance(instance_ref) except KeyError, e: pass @@ -986,22 +1031,25 @@ class LibvirtConnection(object): msg += '%s cannot inherit floating ip.. ' % ec2_id logging.error(_(msg)) + # Restore instance/volume state db.instance_update(context, instance_id, {'state_description': 'running', 'state': power_state.RUNNING, 'host': dest}) - try: - for volume in db.volume_get_all_by_instance(context, instance_id): - db.volume_update(context, - volume['id'], - {'status': 'in-use'}) - except exception.NotFound: - pass + for v in instance_ref['volumes']: + db.volume_update(context, + v['id'], + {'status': 'in-use'}) logging.info(_('Live migrating %s to %s finishes successfully') % (ec2_id, dest)) + msg = _(("""Known error: the below error is nomally occurs.\n""" + """Just check if iinstance is successfully migrated.\n""" + """libvir: QEMU error : Domain not found: no domain """ + """with matching name..""")) + logging.info(msg) class FirewallDriver(object): diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 45d0738a5..76862be27 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -201,6 +201,36 @@ class XenAPIConnection(object): 'username': FLAGS.xenapi_connection_username, 'password': FLAGS.xenapi_connection_password} + def get_cpu_info(self): + """This method is supported only libvirt. """ + return + + def get_vcpu_number(self): + """This method is supported only libvirt. """ + return -1 + + def get_memory_mb(self): + """This method is supported only libvirt..""" + return -1 + + def get_local_gb(self): + """This method is supported only libvirt..""" + return -1 + + def get_hypervisor_type(self): + """This method is supported only libvirt..""" + return + + def get_hypervisor_version(self): + """This method is supported only libvirt..""" + return -1 + + def compare_cpu(self, xml): + raise NotImplementedError('This method is supported only libvirt.') + + def live_migration(self, context, instance_ref, dest): + raise NotImplementedError('This method is supported only libvirt.') + class XenAPISession(object): """The session to invoke XenAPI SDK calls""" diff --git a/nova/volume/manager.py b/nova/volume/manager.py index b4754c607..a568e75f2 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -153,9 +153,6 @@ class VolumeManager(manager.Manager): def check_for_export(self, context, instance_id): """Make sure whether volume is exported.""" if FLAGS.volume_driver == 'nova.volume.driver.AOEDriver': - try: - for vol in self.db.volume_get_all_by_instance(context, - instance_id): - self.driver.check_for_export(context, vol['id']) - except exception.NotFound: - pass + instance_ref = db.instance_get(instance_id) + for v in instance_ref['volumes']: + self.driver.check_for_export(context, v['id']) -- cgit From 523d7788acd6ecb0835dfda73d4cd5540a651a85 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 17 Jan 2011 11:21:56 -0600 Subject: Returning image_metadata from snapshot() --- nova/compute/api.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index d3fa4d786..d5c70e0cd 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -361,7 +361,7 @@ class API(base.Base): :param params: Optional dictionary of arguments to be passed to the compute worker - :retval Result returned by compute worker + :retval: Result returned by compute worker """ if not params: params = {} @@ -374,12 +374,16 @@ class API(base.Base): return rpc.call(context, queue, kwargs) def snapshot(self, context, instance_id, name): - """Snapshot the given instance.""" + """Snapshot the given instance. + + :retval: A dict containing image metadata + """ data = {'name': name, 'is_public': False} image_meta = self.image_service.create(context, data) params = {'image_id': image_meta['id']} self._cast_compute_message('snapshot_instance', context, instance_id, params=params) + return image_meta def reboot(self, context, instance_id): """Reboot the given instance.""" -- cgit From c947f4ed1214c83434436a8e5263233f945aa4f9 Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 17 Jan 2011 11:34:01 -0600 Subject: Fixing whitespace --- nova/compute/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index d5c70e0cd..a6b99c1cb 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -375,8 +375,8 @@ class API(base.Base): def snapshot(self, context, instance_id, name): """Snapshot the given instance. - - :retval: A dict containing image metadata + + :retval: A dict containing image metadata """ data = {'name': name, 'is_public': False} image_meta = self.image_service.create(context, data) -- cgit From 6906137b99181925f091ca547d019499c3bc1701 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 17 Jan 2011 13:36:55 -0500 Subject: Clean up openstack api test fake. --- nova/tests/api/openstack/__init__.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py index 9e183bd0d..14eaaa62c 100644 --- a/nova/tests/api/openstack/__init__.py +++ b/nova/tests/api/openstack/__init__.py @@ -15,23 +15,28 @@ # License for the specific language governing permissions and limitations # under the License. +import webob.dec import unittest from nova import context from nova import flags from nova.api.openstack.ratelimiting import RateLimitingMiddleware from nova.api.openstack.common import limited -from nova.tests.api.fakes import APIStub -from nova import utils +from nova.tests.api.openstack import fakes from webob import Request FLAGS = flags.FLAGS +@webob.dec.wsgify +def simple_wsgi(req): + return "" + + class RateLimitingMiddlewareTest(unittest.TestCase): def test_get_action_name(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) def verify(method, url, action_name): req = Request.blank(url) @@ -61,19 +66,19 @@ class RateLimitingMiddlewareTest(unittest.TestCase): self.assertTrue('Retry-After' in resp.headers) def test_single_action(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) self.exhaust(middleware, 'DELETE', '/servers/4', 'usr1', 100) self.exhaust(middleware, 'DELETE', '/servers/4', 'usr2', 100) def test_POST_servers_action_implies_POST_action(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) self.exhaust(middleware, 'POST', '/images/4', 'usr2', 10) self.assertTrue(set(middleware.limiter._levels) == \ set(['usr1:POST', 'usr1:POST servers', 'usr2:POST'])) def test_POST_servers_action_correctly_ratelimited(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) # Use up all of our "POST" allowance for the minute, 5 times for i in range(5): self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 10) @@ -83,9 +88,9 @@ class RateLimitingMiddlewareTest(unittest.TestCase): self.exhaust(middleware, 'POST', '/servers/4', 'usr1', 0) def test_proxy_ctor_works(self): - middleware = RateLimitingMiddleware(APIStub()) + middleware = RateLimitingMiddleware(simple_wsgi) self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") - middleware = RateLimitingMiddleware(APIStub(), service_host='foobar') + middleware = RateLimitingMiddleware(simple_wsgi, service_host='foobar') self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") -- cgit From a0779f5df2829f91bdc944e7275f44bd831643cc Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Wed, 19 Jan 2011 08:49:17 +0900 Subject: fixed based on reviewer's comment --- nova/api/ec2/cloud.py | 2 -- nova/virt/libvirt_conn.py | 1 + nova/volume/driver.py | 9 +++------ nova/volume/manager.py | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 061f3f70f..c807bc13c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -729,8 +729,6 @@ class CloudController(object): ec2_id = None if (floating_ip_ref['fixed_ip'] and floating_ip_ref['fixed_ip']['instance']): - # modified by masumotok - #instance_id = floating_ip_ref['fixed_ip']['instance']['ec2_id'] instance_id = floating_ip_ref['fixed_ip']['instance']['id'] ec2_id = id_to_ec2_id(instance_id) address_rv = {'public_ip': address, diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 541432ce3..534227339 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1267,6 +1267,7 @@ class NWFilterFirewall(FirewallDriver): # anyway. return + logging.info('ensuring static filters') self._ensure_static_filters() instance_filter_name = self._instance_filter_name(instance) diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 09b93ae91..0d7ad37d5 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -122,7 +122,7 @@ class VolumeDriver(object): """Removes an export for a logical volume.""" raise NotImplementedError() - def discover_volume(self, context, volume): + def discover_volume(self, volume): """Discover volume on a remote host.""" raise NotImplementedError() @@ -184,13 +184,10 @@ class AOEDriver(VolumeDriver): self._try_execute("sudo vblade-persist destroy %s %s" % (shelf_id, blade_id)) - def discover_volume(self, context, volume): + def discover_volume(self, volume): """Discover volume on a remote host.""" self._execute("sudo aoe-discover") self._execute("sudo aoe-stat", check_exit_code=False) - shelf_id, blade_id = self.db.volume_get_shelf_and_blade(context, - volume['id']) - return "/dev/etherd/e%s.%s" % (shelf_id, blade_id) def undiscover_volume(self, _volume): """Undiscover volume on a remote host.""" @@ -296,7 +293,7 @@ class ISCSIDriver(VolumeDriver): iscsi_portal = location.split(",")[0] return (iscsi_name, iscsi_portal) - def discover_volume(self, _context, volume): + def discover_volume(self, volume): """Discover volume on a remote host.""" iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'], volume['host']) diff --git a/nova/volume/manager.py b/nova/volume/manager.py index a568e75f2..da750ab42 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -138,7 +138,7 @@ class VolumeManager(manager.Manager): if volume_ref['host'] == self.host and FLAGS.use_local_volumes: path = self.driver.local_path(volume_ref) else: - path = self.driver.discover_volume(context, volume_ref) + path = self.driver.discover_volume(volume_ref) return path def remove_compute_volume(self, context, volume_id): -- cgit From f935cf4c6e679d1c8eed99bcabe0d4515c2ba254 Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Wed, 19 Jan 2011 09:46:43 +0900 Subject: previous commit breaks volume.driver. fix it.. --- nova/compute/manager.py | 1 - nova/volume/driver.py | 13 ++++++++----- nova/volume/manager.py | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 644f601af..efb5753aa 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -41,7 +41,6 @@ import logging import socket import functools -from nova import context from nova import db from nova import exception from nova import flags diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 0d7ad37d5..cc8809969 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -122,7 +122,7 @@ class VolumeDriver(object): """Removes an export for a logical volume.""" raise NotImplementedError() - def discover_volume(self, volume): + def discover_volume(self, _context, volume): """Discover volume on a remote host.""" raise NotImplementedError() @@ -184,10 +184,13 @@ class AOEDriver(VolumeDriver): self._try_execute("sudo vblade-persist destroy %s %s" % (shelf_id, blade_id)) - def discover_volume(self, volume): + def discover_volume(self, context, volume): """Discover volume on a remote host.""" self._execute("sudo aoe-discover") self._execute("sudo aoe-stat", check_exit_code=False) + shelf_id, blade_id = self.db.volume_get_shelf_and_blade(context, + volume['id']) + return "/dev/etherd/e%s.%s" % (shelf_id, blade_id) def undiscover_volume(self, _volume): """Undiscover volume on a remote host.""" @@ -293,7 +296,7 @@ class ISCSIDriver(VolumeDriver): iscsi_portal = location.split(",")[0] return (iscsi_name, iscsi_portal) - def discover_volume(self, volume): + def discover_volume(self, _context, volume): """Discover volume on a remote host.""" iscsi_name, iscsi_portal = self._get_name_and_portal(volume['name'], volume['host']) @@ -381,7 +384,7 @@ class RBDDriver(VolumeDriver): """Removes an export for a logical volume""" pass - def discover_volume(self, volume): + def discover_volume(self, _context, volume): """Discover volume on a remote host""" return "rbd:%s/%s" % (FLAGS.rbd_pool, volume['name']) @@ -430,7 +433,7 @@ class SheepdogDriver(VolumeDriver): """Removes an export for a logical volume""" pass - def discover_volume(self, volume): + def discover_volume(self, _context, volume): """Discover volume on a remote host""" return "sheepdog:%s" % volume['name'] diff --git a/nova/volume/manager.py b/nova/volume/manager.py index da750ab42..1735d79eb 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -138,7 +138,7 @@ class VolumeManager(manager.Manager): if volume_ref['host'] == self.host and FLAGS.use_local_volumes: path = self.driver.local_path(volume_ref) else: - path = self.driver.discover_volume(volume_ref) + path = self.driver.discover_volume(context, volume_ref) return path def remove_compute_volume(self, context, volume_id): @@ -153,6 +153,6 @@ class VolumeManager(manager.Manager): def check_for_export(self, context, instance_id): """Make sure whether volume is exported.""" if FLAGS.volume_driver == 'nova.volume.driver.AOEDriver': - instance_ref = db.instance_get(instance_id) + instance_ref = self.db.instance_get(instance_id) for v in instance_ref['volumes']: self.driver.check_for_export(context, v['id']) -- cgit From 1dc38833c75d546b1c64d2bcd1f5d9a5bab8836d Mon Sep 17 00:00:00 2001 From: Kei Masumoto Date: Thu, 20 Jan 2011 01:14:23 +0900 Subject: fixed pep8 error --- nova/scheduler/manager.py | 7 +++---- nova/service.py | 8 ++++---- nova/virt/fake.py | 5 +++-- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'nova') diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index f8e4e1613..1cc767a03 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -79,8 +79,8 @@ class SchedulerManager(manager.Manager): if len(services) == 0: return {'ret': False, 'msg': 'No such Host'} - compute = [ s for s in services if s['topic'] == 'compute'] - if 0 == len(compute): + compute = [s for s in services if s['topic'] == 'compute'] + if 0 == len(compute): service_ref = services[0] else: service_ref = compute[0] @@ -90,10 +90,9 @@ class SchedulerManager(manager.Manager): 'memory_mb': service_ref['memory_mb'], 'local_gb': service_ref['local_gb']} - # Getting usage resource information u_resource = {} - instances_ref = db.instance_get_all_by_host(context, + instances_ref = db.instance_get_all_by_host(context, service_ref['host']) if 0 == len(instances_ref): diff --git a/nova/service.py b/nova/service.py index 1acfe3078..a8d52e93b 100644 --- a/nova/service.py +++ b/nova/service.py @@ -119,11 +119,11 @@ class Service(object): def _create_service_ref(self, context): zone = FLAGS.node_availability_zone service_ref = db.service_create(context, - {'host':self.host, - 'binary':self.binary, - 'topic':self.topic, + {'host': self.host, + 'binary': self.binary, + 'topic': self.topic, 'report_count': 0, - 'availability_zone':zone}) + 'availability_zone': zone}) self.service_id = service_ref['id'] def __getattr__(self, key): diff --git a/nova/virt/fake.py b/nova/virt/fake.py index b931e3638..80ae7f34c 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -312,7 +312,7 @@ class FakeConnection(object): def get_cpu_info(self): """This method is supported only libvirt. """ - return + return def get_vcpu_number(self): """This method is supported only libvirt. """ @@ -328,7 +328,7 @@ class FakeConnection(object): def get_hypervisor_type(self): """This method is supported only libvirt..""" - return + return def get_hypervisor_version(self): """This method is supported only libvirt..""" @@ -342,6 +342,7 @@ class FakeConnection(object): """This method is supported only libvirt..""" raise NotImplementedError('This method is supported only libvirt.') + class FakeInstance(object): def __init__(self): -- cgit