diff --git a/rdomanager_oscplugin/tests/v1/overcloud_image/test_overcloud_image.py b/rdomanager_oscplugin/tests/v1/overcloud_image/test_overcloud_image.py index c59c9c478..b7e9a9296 100644 --- a/rdomanager_oscplugin/tests/v1/overcloud_image/test_overcloud_image.py +++ b/rdomanager_oscplugin/tests/v1/overcloud_image/test_overcloud_image.py @@ -13,6 +13,7 @@ # under the License. # +import mock from rdomanager_oscplugin.tests.v1.test_plugin import TestPluginV1 # Load the plugin init module for the plugin list and show commands @@ -39,4 +40,58 @@ class TestOvercloudImageCreate(TestPluginV1): super(TestOvercloudImageCreate, self).setUp() # Get the command object to test - self.cmd = overcloud_image.CreatePlugin(self.app, None) + self.cmd = overcloud_image.CreateOvercloud(self.app, None) + self.app.client_manager.image = mock.Mock() + self.app.client_manager.image.images.create.return_value = \ + mock.Mock(id=10) + self.cmd._read_image_file_pointer = mock.Mock(return_value=b'IMGDATA') + self.cmd._check_file_exists = mock.Mock(return_value=True) + + @mock.patch('subprocess.call') + def test_overcloud_create_images(self, mock_subprocess_call): + parsed_args = self.check_parser(self.cmd, [], []) + + self.cmd.take_action(parsed_args) + + self.assertEqual( + 2, + self.app.client_manager.image.images.delete.call_count + ) + self.assertEqual( + 5, + self.app.client_manager.image.images.create.call_count + ) + self.assertEqual( + [mock.call(data=b'IMGDATA', + name='overcloud-full-vmlinuz', + disk_format='aki', + is_public=True), + mock.call(data=b'IMGDATA', + name='overcloud-full-initrd', + disk_format='ari', + is_public=True), + mock.call(properties={'kernel_id': 10, 'ramdisk_id': 10}, + name='overcloud-full', + data=b'IMGDATA', + container_format='bare', + disk_format='qcow2', + is_public=True), + mock.call(data=b'IMGDATA', + name='bm-deploy-kernel', + disk_format='aki', + is_public=True), + mock.call(data=b'IMGDATA', + name='bm-deploy-ramdisk', + disk_format='ari', + is_public=True) + ], self.app.client_manager.image.images.create.call_args_list + ) + + self.assertEqual(mock_subprocess_call.call_count, 2) + self.assertEqual( + mock_subprocess_call.call_args_list, [ + mock.call('sudo cp -f "./discovery-ramdisk.kernel" ' + '"/tftpboot/discovery.kernel"', shell=True), + mock.call('sudo cp -f "./discovery-ramdisk.initramfs" ' + '"/tftpboot/discovery.ramdisk"', shell=True) + ]) diff --git a/rdomanager_oscplugin/v1/overcloud_image.py b/rdomanager_oscplugin/v1/overcloud_image.py index 778805a27..5264263df 100644 --- a/rdomanager_oscplugin/v1/overcloud_image.py +++ b/rdomanager_oscplugin/v1/overcloud_image.py @@ -16,8 +16,12 @@ """Plugin action implementation""" import logging +import os +import subprocess from cliff import command +from openstackclient.common import exceptions +from openstackclient.common import utils class BuildPlugin(command.Command): @@ -31,11 +35,137 @@ class BuildPlugin(command.Command): pass -class CreatePlugin(command.Command): - """Overcloud Image Create plugin""" +class CreateOvercloud(command.Command): + """Create overcloud glance images from existing image files.""" auth_required = False - log = logging.getLogger(__name__ + ".CreatePlugin") + log = logging.getLogger(__name__ + ".CreateOvercloud") + + def _env_variable_or_set(self, key_name, default_value): + os.environ[key_name] = os.environ.get(key_name, default_value) + + def _delete_image_if_exists(self, image_client, name): + try: + image = utils.find_resource(image_client.images, name) + image_client.images.delete(image.id) + except exceptions.CommandError: + self.log.debug('Image "%s" have already not existed, ' + 'no problem.' % name) + + def _check_file_exists(self, file_path): + if not os.path.isfile(file_path): + print('ERROR: Required file "%s" does not exist' % file_path) + exit(1) + + def _read_image_file_pointer(self, dirname, filename): + filepath = os.path.join(dirname, filename) + self._check_file_exists(filepath) + return open(filepath, 'rb') + + def _copy_file(self, src, dest): + subprocess.call('sudo cp -f "{0}" "{1}"'.format(src, dest), shell=True) + + def _load_image(self, image_path, image_name, image_client): + self.log.debug("uploading images to glance") + + kernel_id = image_client.images.create( + name='%s-vmlinuz' % image_name, + is_public=True, + disk_format='aki', + data=self._read_image_file_pointer(image_path, + '%s.vmlinuz' % image_name) + ).id + + ramdisk_id = image_client.images.create( + name='%s-initrd' % image_name, + is_public=True, + disk_format='ari', + data=self._read_image_file_pointer(image_path, + '%s.initrd' % image_name) + ).id + + image_client.images.create( + name=image_name, + is_public=True, + disk_format='qcow2', + container_format='bare', + properties={'kernel_id': kernel_id, 'ramdisk_id': ramdisk_id}, + data=self._read_image_file_pointer(image_path, + '%s.qcow2' % image_name) + ) + + def get_parser(self, prog_name): + parser = super(CreateOvercloud, self).get_parser(prog_name) + parser.add_argument( + "--image-path", + default='./', + help="Path to directory containing image files", + ) + parser.add_argument( + "--os-image", + default='overcloud-full.qcow2', + help="OpenStack disk image filename", + ) + return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) - pass + image_client = self.app.client_manager.image + + self._env_variable_or_set('DEPLOY_NAME', 'deploy-ramdisk-ironic') + self._env_variable_or_set('DISCOVERY_NAME', 'discovery-ramdisk') + self._env_variable_or_set('TFTP_ROOT', '/tftpboot') + + self.log.debug("check image files") + + image_files = [ + '%s.initramfs' % os.environ['DEPLOY_NAME'], + '%s.kernel' % os.environ['DEPLOY_NAME'], + '%s.initramfs' % os.environ['DISCOVERY_NAME'], + '%s.kernel' % os.environ['DISCOVERY_NAME'], + parsed_args.os_image + ] + + for image in image_files: + self._check_file_exists(os.path.join(parsed_args.image_path, + image)) + + self.log.debug("prepare glance images") + + self._load_image(parsed_args.image_path, + parsed_args.os_image.split('.')[0], + image_client) + + self._delete_image_if_exists(image_client, 'bm_deploy_kernel') + self._delete_image_if_exists(image_client, 'bm_deploy_ramdisk') + + image_client.images.create( + name='bm-deploy-kernel', + is_public=True, + disk_format='aki', + data=self._read_image_file_pointer(parsed_args.image_path, + '%s.kernel' % + os.environ['DEPLOY_NAME']) + ) + + image_client.images.create( + name='bm-deploy-ramdisk', + is_public=True, + disk_format='ari', + data=self._read_image_file_pointer(parsed_args.image_path, + '%s.initramfs' % + os.environ['DEPLOY_NAME']) + ) + + self.log.debug("copy discovery images to TFTP") + + self._copy_file( + os.path.join(parsed_args.image_path, + '%s.kernel' % os.environ['DISCOVERY_NAME']), + os.path.join(os.environ['TFTP_ROOT'], 'discovery.kernel') + ) + + self._copy_file( + os.path.join(parsed_args.image_path, + '%s.initramfs' % os.environ['DISCOVERY_NAME']), + os.path.join(os.environ['TFTP_ROOT'], 'discovery.ramdisk') + ) diff --git a/setup.cfg b/setup.cfg index 6f39e503a..f5ea32e23 100644 --- a/setup.cfg +++ b/setup.cfg @@ -60,5 +60,5 @@ openstack.rdomanager_oscplugin.v1 = baremetal_configure_boot = rdomanager_oscplugin.v1.baremetal:ConfigureBootPlugin overcloud_deploy = rdomanager_oscplugin.v1.overcloud_deploy:DeployPlugin overcloud_image_build = rdomanager_oscplugin.v1.overcloud_image:BuildPlugin - overcloud_image_create = rdomanager_oscplugin.v1.overcloud_image:CreatePlugin + overcloud_image_create = rdomanager_oscplugin.v1.overcloud_image:CreateOvercloud undercloud_install = rdomanager_oscplugin.v1.undercloud:InstallPlugin