diff --git a/nodepool/cmd/nodepoolcmd.py b/nodepool/cmd/nodepoolcmd.py index 1bba30f82..987b7e3df 100644 --- a/nodepool/cmd/nodepoolcmd.py +++ b/nodepool/cmd/nodepoolcmd.py @@ -316,16 +316,19 @@ class NodePoolCmd(NodepoolApp): def info(self): provider_name = self.args.provider - provider_builds = self.zk.getProviderBuilds(provider_name) + provider_uploads = self.zk.getProviderUploads(provider_name) provider_nodes = self.zk.getProviderNodes(provider_name) print("ZooKeeper data for provider %s\n" % provider_name) - print("Image builds:") - t = PrettyTable(['Image Name', 'Build IDs']) + print("Image data:") + t = PrettyTable(['Image Name', 'Build ID', 'Upload IDs']) t.align = 'l' - for image, builds in provider_builds.items(): - t.add_row([image, ','.join(builds)]) + for image in sorted(provider_uploads): + for build in sorted(provider_uploads[image]): + uploads = provider_uploads[image][build] + upload_ids = sorted([u.id for u in uploads]) + t.add_row([image, build, ','.join(upload_ids)]) print(t) print("\nNodes:") diff --git a/nodepool/tests/unit/test_commands.py b/nodepool/tests/unit/test_commands.py index 373b52a53..ec195f336 100644 --- a/nodepool/tests/unit/test_commands.py +++ b/nodepool/tests/unit/test_commands.py @@ -51,7 +51,7 @@ class TestNodepoolCMD(tests.DBTestCase): if col_count: self.assertEquals(len(row), col_count) log.debug(row) - if row[col] == val: + if col < len(row) and row[col] == val: rows_with_val += 1 self.assertEquals(rows_with_val, count) break @@ -320,6 +320,7 @@ class TestNodepoolCMD(tests.DBTestCase): self.useBuilder(configfile) pool.start() p1_image = self.waitForImage('fake-provider', 'fake-image') + p2_image = self.waitForImage('fake-provider2', 'fake-image') p1_nodes = self.waitForNodes('fake-label') p2_nodes = self.waitForNodes('fake-label2') @@ -328,33 +329,47 @@ class TestNodepoolCMD(tests.DBTestCase): # recreate the data. self.replace_config(configfile, 'info_cmd_two_provider_remove.yaml') + IMAGE_NAME_COL = 0 + BUILD_ID_COL = 1 + UPLOAD_ID_COL = 2 + NODE_ID_COL = 0 + # Verify that the second provider image is listed self.assert_listed( configfile, ['info', 'fake-provider2'], - 0, 'fake-image', 1) - - # Verify that the second provider node is listed. + IMAGE_NAME_COL, 'fake-image', 1) self.assert_listed( configfile, ['info', 'fake-provider2'], - 0, p2_nodes[0].id, 1) + BUILD_ID_COL, p2_image.build_id, 1) + self.assert_listed( + configfile, + ['info', 'fake-provider2'], + UPLOAD_ID_COL, p2_image.id, 1) + + # Verify that the second provider node is listed in the second table. + self.assert_listed( + configfile, + ['info', 'fake-provider2'], + NODE_ID_COL, p2_nodes[0].id, 1) # Erase the data for the second provider self.patch_argv( "-c", configfile, 'erase', 'fake-provider2', '--force') nodepoolcmd.main() - # Verify that no build or node for the second provider is listed - # after the previous erase + # Verify that no image or node for the second provider is listed + # after the previous erase. With no build data, the image name should + # not even show up. self.assert_listed( configfile, ['info', 'fake-provider2'], - 0, 'fake-image', 0) + IMAGE_NAME_COL, 'fake-image', 0) self.assert_listed( configfile, ['info', 'fake-provider2'], - 0, p2_nodes[0].id, 0) + NODE_ID_COL, p2_nodes[0].id, 0) # Verify that we did not affect the first provider image = self.waitForImage('fake-provider', 'fake-image') diff --git a/nodepool/tests/unit/test_zk.py b/nodepool/tests/unit/test_zk.py index e2f01e335..06cd6bba2 100644 --- a/nodepool/tests/unit/test_zk.py +++ b/nodepool/tests/unit/test_zk.py @@ -373,6 +373,25 @@ class TestZooKeeper(tests.DBTestCase): d = self.zk.getMostRecentImageUpload(image, provider, zk.READY) self.assertEqual(upload2.state_time, d.state_time) + def test_getProviderUploads(self): + image = "ubuntu-trusty" + provider = "rax" + + build = zk.ImageBuild() + build.state = zk.READY + bnum = self.zk.storeBuild(image, build) + + upload = zk.ImageUpload() + upload.state = zk.READY + upnum = self.zk.storeImageUpload(image, bnum, provider, upload) + + d = self.zk.getProviderUploads(provider) + self.assertIn(image, d) + self.assertIn(bnum, d[image]) + self.assertEqual(1, len(d[image][bnum])) + self.assertIsInstance(d[image][bnum][0], zk.ImageUpload) + self.assertEqual(upnum, d[image][bnum][0].id) + def test_getBuilds_any(self): image = "ubuntu-trusty" path = self.zk._imageBuildsPath(image) diff --git a/nodepool/zk.py b/nodepool/zk.py index 82b58bccf..67f07163d 100644 --- a/nodepool/zk.py +++ b/nodepool/zk.py @@ -2184,6 +2184,35 @@ class ZooKeeper(object): provider_builds[image].append(build) return provider_builds + def getProviderUploads(self, provider_name): + ''' + Get all uploads for a provider for each image. + + :param str provider_name: The provider name. + :returns: A dict, keyed by image name and build ID, of a list of + ImageUpload objects. + ''' + provider_uploads = {} + image_names = self.getImageNames() + for image in image_names: + build_numbers = self.getBuildNumbers(image) + for build in build_numbers: + # If this build is not valid for this provider, move along. + if provider_name not in self.getBuildProviders(image, build): + continue + + # We've determined that we at least have a build for this + # provider so init with an empty upload list. + if image not in provider_uploads: + provider_uploads[image] = {} + provider_uploads[image][build] = [] + + # Add any uploads we might have for this provider. + uploads = self.getUploads(image, build, provider_name) + for upload in uploads: + provider_uploads[image][build].append(upload) + return provider_uploads + def getProviderNodes(self, provider_name): ''' Get all nodes for a provider.