# Copyright (C) 2011-2013 OpenStack Foundation # # 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 logging import threading import time import uuid import openstack.exceptions from nodepool import exceptions from nodepool.driver.openstack.provider import OpenStackProvider from nodepool.driver.fake.handler import FakeNodeRequestHandler from openstack.cloud.exc import OpenStackCloudCreateException class Dummy(object): IMAGE = 'Image' INSTANCE = 'Instance' FLAVOR = 'Flavor' LOCATION = 'Server.Location' PORT = 'Port' def __init__(self, kind, **kw): self.__kind = kind self.__kw = kw for k, v in kw.items(): setattr(self, k, v) try: if self.should_fail: raise openstack.exceptions.OpenStackCloudException( 'This image has SHOULD_FAIL set to True.') if self.over_quota: raise openstack.exceptions.HttpException( message='Quota exceeded for something', http_status=403) except AttributeError: pass def __repr__(self): args = [] for k in self.__kw.keys(): args.append('%s=%s' % (k, getattr(self, k))) args = ' '.join(args) return '<%s %s %s>' % (self.__kind, id(self), args) def __getitem__(self, key, default=None): return getattr(self, key, default) def __setitem__(self, key, value): setattr(self, key, value) def get(self, key, default=None): return getattr(self, key, default) def set(self, key, value): setattr(self, key, value) class FakeOpenStackCloud(object): log = logging.getLogger("nodepool.FakeOpenStackCloud") @staticmethod def _get_quota(): return 100, 20, 1000000 def __init__(self, images=None, networks=None): self.pause_creates = False self._image_list = images if self._image_list is None: self._image_list = [ Dummy( Dummy.IMAGE, id='fake-image-id', status='READY', name='Fake Precise', metadata={}) ] if networks is None: networks = [dict(id='fake-public-network-uuid', name='fake-public-network-name'), dict(id='fake-private-network-uuid', name='fake-private-network-name'), dict(id='fake-ipv6-network-uuid', name='fake-ipv6-network-name')] self.networks = networks self._flavor_list = [ Dummy(Dummy.FLAVOR, id='f1', ram=8192, name='Fake Flavor', vcpus=4), Dummy(Dummy.FLAVOR, id='f2', ram=8192, name='Unreal Flavor', vcpus=4), ] self._azs = ['az1', 'az2'] self._server_list = [] self.max_cores, self.max_instances, self.max_ram = FakeOpenStackCloud.\ _get_quota() self._down_ports = [ Dummy(Dummy.PORT, id='1a', status='DOWN', device_owner="compute:nova"), Dummy(Dummy.PORT, id='2b', status='DOWN', device_owner=None), ] def _get(self, name_or_id, instance_list): self.log.debug("Get %s in %s" % (name_or_id, repr(instance_list))) for instance in instance_list: if isinstance(name_or_id, dict): if instance.id == name_or_id['id']: return instance elif instance.name == name_or_id or instance.id == name_or_id: return instance return None def get_network(self, name_or_id, filters=None): for net in self.networks: if net['id'] == name_or_id or net['name'] == name_or_id: return net return self.networks[0] def _create(self, instance_list, instance_type=Dummy.INSTANCE, done_status='ACTIVE', max_quota=-1, **kw): should_fail = kw.get('SHOULD_FAIL', '').lower() == 'true' nics = kw.get('nics', []) security_groups = kw.get('security_groups', []) addresses = None # if keyword 'ipv6-uuid' is found in provider config, # ipv6 address will be available in public addr dict. for nic in nics: if nic['net-id'] != 'fake-ipv6-network-uuid': continue addresses = dict( public=[dict(version=4, addr='fake'), dict(version=6, addr='fake_v6')], private=[dict(version=4, addr='fake')] ) public_v6 = 'fake_v6' public_v4 = 'fake' private_v4 = 'fake' host_id = 'fake_host_id' interface_ip = 'fake_v6' break if not addresses: addresses = dict( public=[dict(version=4, addr='fake')], private=[dict(version=4, addr='fake')] ) public_v6 = '' public_v4 = 'fake' private_v4 = 'fake' host_id = 'fake' interface_ip = 'fake' over_quota = False if (instance_type == Dummy.INSTANCE and self.max_instances > -1 and len(instance_list) >= self.max_instances): over_quota = True az = kw.get('availability_zone') if az and az not in self._azs: raise openstack.exceptions.BadRequestException( message='The requested availability zone is not available', http_status=400) s = Dummy(instance_type, id=uuid.uuid4().hex, name=kw['name'], status='BUILD', adminPass='fake', addresses=addresses, public_v4=public_v4, public_v6=public_v6, private_v4=private_v4, host_id=host_id, interface_ip=interface_ip, security_groups=security_groups, location=Dummy(Dummy.LOCATION, zone=kw.get('az')), metadata=kw.get('meta', {}), manager=self, key_name=kw.get('key_name', None), should_fail=should_fail, over_quota=over_quota, event=threading.Event()) instance_list.append(s) t = threading.Thread(target=self._finish, name='FakeProvider create', args=(s, 0.1, done_status)) t.start() return s def _delete(self, name_or_id, instance_list): self.log.debug("Delete from %s" % (repr(instance_list),)) instance = None for maybe in instance_list: if maybe.name == name_or_id or maybe.id == name_or_id: instance = maybe if instance: instance_list.remove(instance) self.log.debug("Deleted from %s" % (repr(instance_list),)) def _finish(self, obj, delay, status): self.log.debug("Pause creates %s", self.pause_creates) if self.pause_creates: self.log.debug("Pausing") obj.event.wait() self.log.debug("Continuing") else: time.sleep(delay) obj.status = status def create_image(self, **kwargs): return self._create( self._image_list, instance_type=Dummy.IMAGE, done_status='READY', **kwargs) def get_image(self, name_or_id): return self._get(name_or_id, self._image_list) def list_images(self): return self._image_list def delete_image(self, name_or_id): if not name_or_id: raise Exception('name_or_id is Empty') self._delete(name_or_id, self._image_list) def create_image_snapshot(self, name, server, **metadata): # XXX : validate metadata? return self._create( self._image_list, instance_type=Dummy.IMAGE, name=name, **metadata) def list_flavors(self, get_extra=False): return self._flavor_list def get_openstack_vars(self, server): server.public_v4 = 'fake' server.public_v6 = 'fake' server.private_v4 = 'fake' server.host_id = 'fake' server.interface_ip = 'fake' return server def create_server(self, **kw): return self._create(self._server_list, **kw) def get_server(self, name_or_id): result = self._get(name_or_id, self._server_list) return result def _clean_floating_ip(self, server): server.public_v4 = '' server.public_v6 = '' server.interface_ip = server.private_v4 return server def wait_for_server(self, server, **kwargs): while server.status == 'BUILD': time.sleep(0.1) auto_ip = kwargs.get('auto_ip') if not auto_ip: server = self._clean_floating_ip(server) return server def list_servers(self): return self._server_list def delete_server(self, name_or_id, delete_ips=True): self._delete(name_or_id, self._server_list) def list_availability_zone_names(self): return self._azs.copy() def get_compute_limits(self): return Dummy( 'limits', max_total_cores=self.max_cores, max_total_instances=self.max_instances, max_total_ram_size=self.max_ram, total_cores_used=4 * len(self._server_list), total_instances_used=len(self._server_list), total_ram_used=8192 * len(self._server_list) ) def list_ports(self, filters=None): if filters and filters.get('status') == 'DOWN': return self._down_ports return [] def delete_port(self, port_id): tmp_ports = [] for port in self._down_ports: if port.id != port_id: tmp_ports.append(port) else: self.log.debug("Deleted port ID: %s", port_id) self._down_ports = tmp_ports class FakeUploadFailCloud(FakeOpenStackCloud): log = logging.getLogger("nodepool.FakeUploadFailCloud") def __init__(self, times_to_fail=None): super(FakeUploadFailCloud, self).__init__() self.times_to_fail = times_to_fail self.times_failed = 0 def create_image(self, **kwargs): if self.times_to_fail is None: raise exceptions.BuilderError("Test fail image upload.") self.times_failed += 1 if self.times_failed <= self.times_to_fail: raise exceptions.BuilderError("Test fail image upload.") else: return super(FakeUploadFailCloud, self).create_image(**kwargs) class FakeLaunchAndDeleteFailCloud(FakeOpenStackCloud): log = logging.getLogger("nodepool.FakeLaunchAndDeleteFailCloud") def __init__(self, times_to_fail=None): super(FakeLaunchAndDeleteFailCloud, self).__init__() self.times_to_fail_delete = times_to_fail self.times_to_fail_launch = times_to_fail self.times_failed_delete = 0 self.times_failed_launch = 0 self.launch_success = False self.delete_success = False def wait_for_server(self, **kwargs): if self.times_to_fail_launch is None: raise Exception("Test fail server launch.") if self.times_failed_launch < self.times_to_fail_launch: self.times_failed_launch += 1 raise exceptions.ServerDeleteException("Test fail server launch.") else: self.launch_success = True return super(FakeLaunchAndDeleteFailCloud, self).wait_for_server(**kwargs) def delete_server(self, *args, **kwargs): if self.times_to_fail_delete is None: raise exceptions.ServerDeleteException("Test fail server delete.") if self.times_failed_delete < self.times_to_fail_delete: self.times_failed_delete += 1 raise exceptions.ServerDeleteException("Test fail server delete.") else: self.delete_success = True return super(FakeLaunchAndDeleteFailCloud, self).delete_server(*args, **kwargs) class FakeProvider(OpenStackProvider): fake_cloud = FakeOpenStackCloud def __init__(self, provider, use_taskmanager): self.createServer_fails = 0 self.createServer_fails_with_external_id = 0 self.__client = FakeProvider.fake_cloud() super(FakeProvider, self).__init__(provider, use_taskmanager) def _getClient(self): return self.__client def createServer(self, *args, **kwargs): while self.createServer_fails: self.createServer_fails -= 1 raise Exception("Expected createServer exception") while self.createServer_fails_with_external_id: self.createServer_fails_with_external_id -= 1 raise OpenStackCloudCreateException('server', 'fakeid') return super(FakeProvider, self).createServer(*args, **kwargs) def getRequestHandler(self, poolworker, request): return FakeNodeRequestHandler(poolworker, request)