def test_storeImageUpload(self): image = "ubuntu-trusty" provider = "rax" bnum = self.zk.storeBuild(image, zk.ImageBuild()) up1 = self.zk.storeImageUpload(image, bnum, provider, zk.ImageUpload()) up2 = self.zk.storeImageUpload(image, bnum, provider, zk.ImageUpload()) self.assertLess(int(up1), int(up2))
def _deleteUpload(self, upload): deleted = False if upload.state != zk.DELETING: if not self._inProgressUpload(upload): data = zk.ImageUpload() data.state = zk.DELETING self._zk.storeImageUpload(upload.image_name, upload.build_id, upload.provider_name, data, upload.id) deleted = True if upload.state == zk.DELETING or deleted: manager = self._config.provider_managers[upload.provider_name] try: # It is possible we got this far, but don't actually have an # external_name. This could mean that zookeeper and cloud # provider are some how out of sync. if upload.external_name: base = "-".join([upload.image_name, upload.build_id]) self.log.info("Deleting image build %s from %s" % (base, upload.provider_name)) manager.deleteImage(upload.external_name) except Exception: self.log.exception( "Unable to delete image %s from %s:", upload.external_name, upload.provider_name) else: self.log.debug("Deleting image upload: %s", upload) self._zk.deleteUpload(upload.image_name, upload.build_id, upload.provider_name, upload.id)
def test_store_and_get_image_upload(self): image = "ubuntu-trusty" provider = "rax" orig_data = zk.ImageUpload() orig_data.external_id = "deadbeef" orig_data.state = zk.READY orig_data.format = "qcow2" build_number = self.zk.storeBuild(image, zk.ImageBuild()) upload_id = self.zk.storeImageUpload(image, build_number, provider, orig_data) data = self.zk.getImageUpload(image, build_number, provider, upload_id) self.assertEqual(upload_id, data.id) self.assertEqual(orig_data.external_id, data.external_id) self.assertEqual(orig_data.state, data.state) self.assertEqual(orig_data.state_time, data.state_time) self.assertEqual(orig_data.format, data.format) self.assertEqual(self.zk.getBuildProviders("ubuntu-trusty", build_number), [provider]) self.assertEqual(self.zk.getImageUploadNumbers("ubuntu-trusty", build_number, provider), [upload_id])
def test_getUploads_any(self): path = self.zk._imageUploadPath("trusty", "000", "rax") v1 = zk.ImageUpload() v1.state = zk.READY v2 = zk.ImageUpload() v2.state = zk.UPLOADING v3 = zk.ImageUpload() v3.state = zk.FAILED v4 = zk.ImageUpload() v4.state = zk.DELETING self.zk.client.create(path + "/1", value=v1.serialize(), makepath=True) self.zk.client.create(path + "/2", value=v2.serialize(), makepath=True) self.zk.client.create(path + "/3", value=v3.serialize(), makepath=True) self.zk.client.create(path + "/4", value=v4.serialize(), makepath=True) self.zk.client.create(path + "/lock", makepath=True) matches = self.zk.getUploads("trusty", "000", "rax", None) self.assertEqual(4, len(matches))
def test_storeImageUpload_invalid_build(self): image = "ubuntu-trusty" build_number = "0000000001" provider = "rax" orig_data = zk.ImageUpload() with testtools.ExpectedException(npe.ZKException, "Cannot find build .*"): self.zk.storeImageUpload(image, build_number, provider, orig_data)
def test_ImageUpload_toDict(self): o = zk.ImageUpload('0001', '0003') o.external_id = 'DEADBEEF' o.external_name = 'trusty' d = o.toDict() self.assertNotIn('id', d) self.assertNotIn('build_id', d) self.assertNotIn('provider_name', d) self.assertNotIn('image_name', d) self.assertEqual(o.external_id, d['external_id']) self.assertEqual(o.external_name, d['external_name'])
def test_deleteBuild_with_uploads(self): image = 'trusty' provider = 'rax' build = zk.ImageBuild() build.state = zk.READY bnum = self.zk.storeBuild(image, build) upload = zk.ImageUpload() upload.state = zk.READY self.zk.storeImageUpload(image, bnum, provider, upload) self.assertFalse(self.zk.deleteBuild(image, bnum))
def test_getMostRecentImageUpload(self): image = "ubuntu-trusty" provider = "rax" build1 = zk.ImageBuild() build1.state = zk.READY build2 = zk.ImageBuild() build2.state = zk.READY build2.state_time = build1.state_time + 10 bnum1 = self.zk.storeBuild(image, build1) bnum2 = self.zk.storeBuild(image, build2) upload1 = zk.ImageUpload() upload1.state = zk.READY upload2 = zk.ImageUpload() upload2.state = zk.READY upload2.state_time = upload1.state_time + 10 self.zk.storeImageUpload(image, bnum1, provider, upload1) self.zk.storeImageUpload(image, bnum2, provider, upload2) d = self.zk.getMostRecentImageUpload(image, provider, zk.READY) self.assertEqual(upload2.state_time, d.state_time)
def test_getMostRecentBuildImageUploads_any_state(self): image = "ubuntu-trusty" provider = "rax" build = {'state': zk.READY, 'state_time': int(time.time())} up1 = zk.ImageUpload() up1.state = zk.READY up2 = zk.ImageUpload() up2.state = zk.READY up2.state_time = up1.state_time + 10 up3 = zk.ImageUpload() up3.state = zk.UPLOADING up3.state_time = up2.state_time + 10 bnum = self.zk.storeBuild(image, zk.ImageBuild.fromDict(build)) self.zk.storeImageUpload(image, bnum, provider, up1) self.zk.storeImageUpload(image, bnum, provider, up2) up3_id = self.zk.storeImageUpload(image, bnum, provider, up3) # up3 should be the most recent upload, regardless of state data = self.zk.getMostRecentBuildImageUploads(1, image, bnum, provider, None) self.assertNotEqual([], data) self.assertEqual(1, len(data)) self.assertEqual(data[0].id, up3_id)
def test_getMostRecentBuildImageUploads_with_state(self): image = "ubuntu-trusty" provider = "rax" build = {'state': zk.READY, 'state_time': int(time.time())} up1 = zk.ImageUpload() up1.state = zk.READY up2 = zk.ImageUpload() up2.state = zk.READY up2.state_time = up1.state_time + 10 up3 = zk.ImageUpload() up3.state = zk.DELETING up3.state_time = up2.state_time + 10 bnum = self.zk.storeBuild(image, zk.ImageBuild.fromDict(build)) self.zk.storeImageUpload(image, bnum, provider, up1) up2_id = self.zk.storeImageUpload(image, bnum, provider, up2) self.zk.storeImageUpload(image, bnum, provider, up3) # up2 should be the most recent 'ready' upload data = self.zk.getMostRecentBuildImageUploads(1, image, bnum, provider, zk.READY) self.assertNotEqual([], data) self.assertEqual(1, len(data)) self.assertEqual(data[0].id, up2_id)
def test_ImageUpload_toDict(self): o = zk.ImageUpload('0001', '0003') o.state = zk.UPLOADING o.external_id = 'DEADBEEF' o.external_name = 'trusty' o.format = 'qcow2' d = o.toDict() self.assertNotIn('id', d) self.assertNotIn('build_id', d) self.assertNotIn('provider_name', d) self.assertNotIn('image_name', d) self.assertEqual(o.state, d['state']) self.assertEqual(o.state_time, d['state_time']) self.assertEqual(o.external_id, d['external_id']) self.assertEqual(o.external_name, d['external_name']) self.assertEqual(o.format, d['format'])
def _uploadImage(self, build_id, upload_id, image_name, images, provider, username): ''' Upload a local DIB image build to a provider. :param str build_id: Unique ID of the image build to upload. :param str upload_id: Unique ID of the upload. :param str image_name: Name of the diskimage. :param list images: A list of DibImageFile objects from this build that available for uploading. :param provider: The provider from the parsed config file. :param username: ''' start_time = time.time() timestamp = int(start_time) image = None for i in images: if provider.image_type == i.extension: image = i break if not image: raise exceptions.BuilderInvalidCommandError( "Unable to find image file of type %s for id %s to upload" % (provider.image_type, build_id) ) self.log.debug("Found image file of type %s for image id: %s" % (image.extension, image.image_id)) filename = image.to_path(self._config.imagesdir, with_extension=True) ext_image_name = provider.image_name_format.format( image_name=image_name, timestamp=str(timestamp) ) self.log.info("Uploading DIB image build %s from %s to %s" % (build_id, filename, provider.name)) manager = self._config.provider_managers[provider.name] provider_image = provider.diskimages.get(image_name) if provider_image is None: raise exceptions.BuilderInvalidCommandError( "Could not find matching provider image for %s" % image_name ) meta = provider_image.meta.copy() meta['nodepool_build_id'] = build_id meta['nodepool_upload_id'] = upload_id try: external_id = manager.uploadImage( ext_image_name, filename, image_type=image.extension, meta=meta, md5=image.md5, sha256=image.sha256, ) except Exception: self.log.exception("Failed to upload image %s to provider %s" % (image_name, provider.name)) data = zk.ImageUpload() data.state = zk.FAILED return data if self._statsd: dt = int((time.time() - start_time) * 1000) key = 'nodepool.image_update.%s.%s' % (image_name, provider.name) self._statsd.timing(key, dt) self._statsd.incr(key) base = "-".join([image_name, build_id]) self.log.info("Image build %s in %s is ready" % (base, provider.name)) data = zk.ImageUpload() data.state = zk.READY data.external_id = external_id data.external_name = ext_image_name data.format = image.extension data.username = username return data
def _checkProviderImageUpload(self, provider, image): ''' The main body of _checkForProviderUploads. This encapsulates checking whether an image for a provider should be uploaded and performing the upload. It is a separate function so that exception handling can treat all provider-image uploads indepedently. :returns: True if an upload was attempted, False otherwise. ''' # Check if image uploads are paused. if provider.diskimages.get(image.name).pause: return False # Search for the most recent 'ready' image build builds = self._zk.getMostRecentBuilds(1, image.name, zk.READY) if not builds: return False build = builds[0] # Search for locally built images. The image name and build # sequence ID is used to name the image. local_images = DibImageFile.from_image_id( self._config.imagesdir, "-".join([image.name, build.id])) if not local_images: return False # See if this image has already been uploaded upload = self._zk.getMostRecentBuildImageUploads( 1, image.name, build.id, provider.name, zk.READY) if upload: return False # See if this provider supports the available image formats if provider.image_type not in build.formats: return False try: with self._zk.imageUploadLock( image.name, build.id, provider.name, blocking=False ): # Verify once more that it hasn't been uploaded since the # last check. upload = self._zk.getMostRecentBuildImageUploads( 1, image.name, build.id, provider.name, zk.READY) if upload: return False # NOTE: Due to the configuration file disagreement issue # (the copy we have may not be current), we try to verify # that another thread isn't trying to delete this build just # before we upload. b = self._zk.getBuild(image.name, build.id) if b.state == zk.DELETING: return False # New upload number with initial state 'uploading' data = zk.ImageUpload() data.state = zk.UPLOADING data.username = build.username upnum = self._zk.storeImageUpload( image.name, build.id, provider.name, data) data = self._uploadImage(build.id, upnum, image.name, local_images, provider, build.username) # Set final state self._zk.storeImageUpload(image.name, build.id, provider.name, data, upnum) return True except exceptions.ZKLockException: # Lock is already held. Skip it. return False