Beispiel #1
0
    def _heat_deploy(self,
                     stack,
                     stack_name,
                     template_path,
                     parameters,
                     env_files,
                     timeout,
                     tht_root,
                     env,
                     update_plan_only,
                     run_validations,
                     skip_deploy_identifier,
                     plan_env_file,
                     deployment_options=None):
        """Verify the Baremetal nodes are available and do a stack update"""

        if stack:
            self.log.debug(
                "Checking compatibilities of neutron drivers for {0}".format(
                    stack_name))
            msg = update.check_neutron_mechanism_drivers(
                env, stack, self.object_client, stack_name)
            if msg:
                raise oscexc.CommandError(msg)

        self.log.debug("Getting template contents from plan %s" % stack_name)
        # We need to reference the plan here, not the local
        # tht root, as we need template_object to refer to
        # the rendered overcloud.yaml, not the tht_root overcloud.j2.yaml
        # FIXME(shardy) we need to move more of this into mistral actions
        plan_yaml_path = os.path.relpath(template_path, tht_root)

        # heatclient template_utils needs a function that can
        # retrieve objects from a container by name/path
        def do_object_request(method='GET', object_path=None):
            obj = self.object_client.get_object(stack_name, object_path)
            return obj and obj[1]

        template_files, template = template_utils.get_template_contents(
            template_object=plan_yaml_path, object_request=do_object_request)

        files = dict(list(template_files.items()) + list(env_files.items()))

        moved_files = self._upload_missing_files(stack_name, files, tht_root)
        self._process_and_upload_environment(stack_name, env, moved_files,
                                             tht_root)

        # Invokes the workflows specified in plan environment file
        if plan_env_file:
            workflow_params.invoke_plan_env_workflows(
                self.clients,
                stack_name,
                plan_env_file,
                verbosity=utils.playbook_verbosity(self=self))

        workflow_params.check_deprecated_parameters(self.clients, stack_name)

        if not update_plan_only:
            print("Deploying templates in the directory {0}".format(
                os.path.abspath(tht_root)))
            deployment.deploy_and_wait(
                log=self.log,
                clients=self.clients,
                stack=stack,
                plan_name=stack_name,
                verbose_level=utils.playbook_verbosity(self=self),
                timeout=timeout,
                run_validations=run_validations,
                skip_deploy_identifier=skip_deploy_identifier,
                deployment_options=deployment_options)
Beispiel #2
0
    def take_action(self, parsed_args):
        # Copied from overwritten method!

        client = self.app.client_manager.network
        obj = client.find_port(parsed_args.port, ignore_missing=False)
        # SDK ignores update() if it receives a modified obj and attrs
        # To handle the same tmp_obj is created in all take_action of
        # Unset* classes
        tmp_fixed_ips = copy.deepcopy(obj.fixed_ips)
        tmp_binding_profile = copy.deepcopy(obj.binding_profile)
        tmp_secgroups = copy.deepcopy(obj.security_group_ids)
        tmp_addr_pairs = copy.deepcopy(obj.allowed_address_pairs)
        port._prepare_fixed_ips(self.app.client_manager, parsed_args)
        attrs = {}
        if parsed_args.fixed_ip:
            try:
                for ip in parsed_args.fixed_ip:
                    tmp_fixed_ips.remove(ip)
            except ValueError:
                msg = _("Port does not contain fixed-ip %s") % ip
                raise exceptions.CommandError(msg)
            attrs['fixed_ips'] = tmp_fixed_ips
        if parsed_args.binding_profile:
            try:
                for key in parsed_args.binding_profile:
                    del tmp_binding_profile[key]
            except KeyError:
                msg = _("Port does not contain binding-profile %s") % key
                raise exceptions.CommandError(msg)
            attrs['binding:profile'] = tmp_binding_profile
        if parsed_args.security_group_ids:
            try:
                for sg in parsed_args.security_group_ids:
                    sg_id = client.find_security_group(sg,
                                                       ignore_missing=False).id
                    tmp_secgroups.remove(sg_id)
            except ValueError:
                msg = _("Port does not contain security group %s") % sg
                raise exceptions.CommandError(msg)
            attrs['security_group_ids'] = tmp_secgroups
        if parsed_args.allowed_address_pairs:
            try:
                for addr in port._convert_address_pairs(parsed_args):
                    tmp_addr_pairs.remove(addr)
            except ValueError:
                msg = _("Port does not contain allowed-address-pair %s") % addr
                raise exceptions.CommandError(msg)
            attrs['allowed_address_pairs'] = tmp_addr_pairs
        if parsed_args.qos_policy:
            attrs['qos_policy_id'] = None
        if parsed_args.data_plane_status:
            attrs['data_plane_status'] = None

        # Nuage specific attributes
        self._handle_nuage_specific_attributes(
            parsed_args, attrs, obj, self.app.client_manager.nuageclient)

        if attrs:
            client.update_port(obj, **attrs)

        # tags is a subresource and it needs to be updated separately.
        _tag.update_tags_for_unset(client, obj, parsed_args)
    def test_image_create_file(self, mock_open):
        mock_file = mock.Mock(name='File')
        mock_open.return_value = mock_file
        mock_open.read.return_value = image_fakes.image_data
        mock_exception = {
            'find.side_effect': exceptions.CommandError('x'),
            'get.side_effect': exceptions.CommandError('x'),
        }
        self.images_mock.configure_mock(**mock_exception)

        arglist = [
            '--file',
            'filer',
            '--unprotected',
            '--public',
            '--property',
            'Alpha=1',
            '--property',
            'Beta=2',
            image_fakes.image_name,
        ]
        verifylist = [
            ('file', 'filer'),
            ('protected', False),
            ('unprotected', True),
            ('public', True),
            ('private', False),
            ('properties', {
                'Alpha': '1',
                'Beta': '2'
            }),
            ('name', image_fakes.image_name),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        # In base command class ShowOne in cliff, abstract method take_action()
        # returns a two-part tuple with a tuple of column names and a tuple of
        # data to be shown.
        columns, data = self.cmd.take_action(parsed_args)

        # Ensure input file is opened
        mock_open.assert_called_with('filer', 'rb')

        # Ensure the input file is closed
        mock_file.close.assert_called_with()

        # ImageManager.get(name) not to be called since update action exists
        self.images_mock.get.assert_not_called()

        # ImageManager.create(name=, **)
        self.images_mock.create.assert_called_with(
            name=image_fakes.image_name,
            container_format=image.DEFAULT_CONTAINER_FORMAT,
            disk_format=image.DEFAULT_DISK_FORMAT,
            protected=False,
            is_public=True,
            properties={
                'Alpha': '1',
                'Beta': '2',
            },
            data=mock_file,
        )

        # Verify update() was not called, if it was show the args
        self.assertEqual(self.images_mock.update.call_args_list, [])

        self.assertEqual(image_fakes.IMAGE_columns, columns)
        self.assertEqual(image_fakes.IMAGE_data, data)
    def take_action_network(self, client, parsed_args):
        # Get the security group ID to hold the rule.
        security_group_id = client.find_security_group(parsed_args.group,
                                                       ignore_missing=False).id

        # Build the create attributes.
        attrs = {}
        attrs['protocol'] = self._get_protocol(parsed_args)

        if parsed_args.description is not None:
            attrs['description'] = parsed_args.description

        # NOTE(rtheis): A direction must be specified and ingress
        # is the default.
        if parsed_args.ingress or not parsed_args.egress:
            attrs['direction'] = 'ingress'
        if parsed_args.egress:
            attrs['direction'] = 'egress'

        # NOTE(rtheis): Use ethertype specified else default based
        # on IP protocol.
        attrs['ethertype'] = self._get_ethertype(parsed_args,
                                                 attrs['protocol'])

        # NOTE(rtheis): Validate the port range and ICMP type and code.
        # It would be ideal if argparse could do this.
        if parsed_args.dst_port and (parsed_args.icmp_type
                                     or parsed_args.icmp_code):
            msg = _('Argument --dst-port not allowed with arguments '
                    '--icmp-type and --icmp-code')
            raise exceptions.CommandError(msg)
        if parsed_args.icmp_type is None and parsed_args.icmp_code is not None:
            msg = _('Argument --icmp-type required with argument --icmp-code')
            raise exceptions.CommandError(msg)
        is_icmp_protocol = _is_icmp_protocol(attrs['protocol'])
        if not is_icmp_protocol and (parsed_args.icmp_type
                                     or parsed_args.icmp_code):
            msg = _('ICMP IP protocol required with arguments '
                    '--icmp-type and --icmp-code')
            raise exceptions.CommandError(msg)
        # NOTE(rtheis): For backwards compatibility, continue ignoring
        # the destination port range when an ICMP IP protocol is specified.
        if parsed_args.dst_port and not is_icmp_protocol:
            attrs['port_range_min'] = parsed_args.dst_port[0]
            attrs['port_range_max'] = parsed_args.dst_port[1]
        if parsed_args.icmp_type is not None and parsed_args.icmp_type >= 0:
            attrs['port_range_min'] = parsed_args.icmp_type
        if parsed_args.icmp_code is not None and parsed_args.icmp_code >= 0:
            attrs['port_range_max'] = parsed_args.icmp_code

        if parsed_args.remote_group is not None:
            attrs['remote_group_id'] = client.find_security_group(
                parsed_args.remote_group, ignore_missing=False).id
        elif parsed_args.remote_address_group is not None:
            attrs['remote_address_group_id'] = client.find_address_group(
                parsed_args.remote_address_group, ignore_missing=False).id
        elif parsed_args.remote_ip is not None:
            attrs['remote_ip_prefix'] = parsed_args.remote_ip
        elif attrs['ethertype'] == 'IPv4':
            attrs['remote_ip_prefix'] = '0.0.0.0/0'
        elif attrs['ethertype'] == 'IPv6':
            attrs['remote_ip_prefix'] = '::/0'
        attrs['security_group_id'] = security_group_id
        if parsed_args.project is not None:
            identity_client = self.app.client_manager.identity
            project_id = identity_common.find_project(
                identity_client,
                parsed_args.project,
                parsed_args.project_domain,
            ).id
            attrs['project_id'] = project_id

        attrs.update(self._parse_extra_properties(
            parsed_args.extra_properties))

        # Create and show the security group rule.
        obj = client.create_security_group_rule(**attrs)
        display_columns, columns = _get_columns(obj)
        data = utils.get_item_properties(obj, columns)
        return (display_columns, data)
Beispiel #5
0
    def test_image_reserve_options(self, mock_open):
        mock_file = mock.MagicMock(name='File')
        mock_open.return_value = mock_file
        mock_open.read.return_value = None
        mock_exception = {
            'find.side_effect': exceptions.CommandError('x'),
        }
        self.images_mock.configure_mock(**mock_exception)
        arglist = [
            '--container-format', 'ovf',
            '--disk-format', 'ami',
            '--min-disk', '10',
            '--min-ram', '4',
            ('--protected'
                if self.new_image.protected else '--unprotected'),
            ('--private'
                if self.new_image.visibility == 'private' else '--public'),
            '--project', self.new_image.owner,
            '--project-domain', self.domain.id,
            self.new_image.name,
        ]
        verifylist = [
            ('container_format', 'ovf'),
            ('disk_format', 'ami'),
            ('min_disk', 10),
            ('min_ram', 4),
            ('protected', self.new_image.protected),
            ('unprotected', not self.new_image.protected),
            ('public', self.new_image.visibility == 'public'),
            ('private', self.new_image.visibility == 'private'),
            ('project', self.new_image.owner),
            ('project_domain', self.domain.id),
            ('name', self.new_image.name),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        # In base command class ShowOne in cliff, abstract method take_action()
        # returns a two-part tuple with a tuple of column names and a tuple of
        # data to be shown.
        columns, data = self.cmd.take_action(parsed_args)

        # ImageManager.create(name=, **)
        self.images_mock.create.assert_called_with(
            name=self.new_image.name,
            container_format='ovf',
            disk_format='ami',
            min_disk=10,
            min_ram=4,
            owner=self.project.id,
            protected=self.new_image.protected,
            visibility=self.new_image.visibility,
        )

        # Verify update() was not called, if it was show the args
        self.assertEqual(self.images_mock.update.call_args_list, [])

        self.images_mock.upload.assert_called_with(
            mock.ANY, mock.ANY,
        )

        self.assertEqual(
            image_fakes.FakeImage.get_image_columns(self.new_image),
            columns)
        self.assertEqual(
            image_fakes.FakeImage.get_image_data(self.new_image),
            data)
    def take_action(self, parsed_args):
        image_client = self.app.client_manager.image
        image = image_client.find_image(parsed_args.image,
                                        ignore_missing=False)

        kwargs = {}
        tagret = 0
        propret = 0
        if parsed_args.tags:
            for k in parsed_args.tags:
                try:
                    image_client.remove_tag(image.id, k)
                except Exception:
                    LOG.error(
                        _("tag unset failed, '%s' is a "
                          "nonexistent tag "), k)
                    tagret += 1

        if parsed_args.properties:
            for k in parsed_args.properties:
                if k in image:
                    kwargs[k] = None
                elif k in image.properties:
                    # Since image is an "evil" object from SDK POV we need to
                    # pass modified properties object, so that SDK can figure
                    # out, what was changed inside
                    # NOTE: ping gtema to improve that in SDK
                    new_props = kwargs.get('properties',
                                           image.get('properties').copy())
                    new_props.pop(k, None)
                    kwargs['properties'] = new_props
                else:
                    LOG.error(
                        _("property unset failed, '%s' is a "
                          "nonexistent property "), k)
                    propret += 1

            # We must give to update a current image for the reference on what
            # has changed
            image_client.update_image(image, **kwargs)

        tagtotal = len(parsed_args.tags)
        proptotal = len(parsed_args.properties)
        if (tagret > 0 and propret > 0):
            msg = (
                _("Failed to unset %(tagret)s of %(tagtotal)s tags,"
                  "Failed to unset %(propret)s of %(proptotal)s properties.") %
                {
                    'tagret': tagret,
                    'tagtotal': tagtotal,
                    'propret': propret,
                    'proptotal': proptotal
                })
            raise exceptions.CommandError(msg)
        elif tagret > 0:
            msg = (_("Failed to unset %(tagret)s of %(tagtotal)s tags.") % {
                'tagret': tagret,
                'tagtotal': tagtotal
            })
            raise exceptions.CommandError(msg)
        elif propret > 0:
            msg = (_("Failed to unset %(propret)s of %(proptotal)s"
                     " properties.") % {
                         'propret': propret,
                         'proptotal': proptotal
                     })
            raise exceptions.CommandError(msg)
    def take_action(self, parsed_args):
        identity_client = self.app.client_manager.identity
        image_client = self.app.client_manager.image

        for deadopt in self.deadopts:
            if getattr(parsed_args, deadopt.replace('-', '_'), None):
                raise exceptions.CommandError(
                    _("ERROR: --%s was given, which is an Image v1 option"
                      " that is no longer supported in Image v2") % deadopt)

        kwargs = {}
        copy_attrs = ('architecture', 'container_format', 'disk_format',
                      'file', 'instance_id', 'kernel_id', 'locations',
                      'min_disk', 'min_ram', 'name', 'os_distro', 'os_version',
                      'prefix', 'progress', 'ramdisk_id', 'tags', 'visibility')
        for attr in copy_attrs:
            if attr in parsed_args:
                val = getattr(parsed_args, attr, None)
                if val is not None:
                    # Only include a value in kwargs for attributes that are
                    # actually present on the command line
                    kwargs[attr] = val

        # Properties should get flattened into the general kwargs
        if getattr(parsed_args, 'properties', None):
            for k, v in six.iteritems(parsed_args.properties):
                kwargs[k] = str(v)

        # Handle exclusive booleans with care
        # Avoid including attributes in kwargs if an option is not
        # present on the command line.  These exclusive booleans are not
        # a single value for the pair of options because the default must be
        # to do nothing when no options are present as opposed to always
        # setting a default.
        if parsed_args.protected:
            kwargs['protected'] = True
        if parsed_args.unprotected:
            kwargs['protected'] = False
        if parsed_args.public:
            kwargs['visibility'] = 'public'
        if parsed_args.private:
            kwargs['visibility'] = 'private'
        if parsed_args.community:
            kwargs['visibility'] = 'community'
        if parsed_args.shared:
            kwargs['visibility'] = 'shared'
        # Handle deprecated --owner option
        project_arg = parsed_args.project
        if parsed_args.owner:
            project_arg = parsed_args.owner
            LOG.warning(
                _('The --owner option is deprecated, '
                  'please use --project instead.'))
        project_id = None
        if project_arg:
            project_id = common.find_project(
                identity_client,
                project_arg,
                parsed_args.project_domain,
            ).id
            kwargs['owner'] = project_id

        image = utils.find_resource(image_client.images, parsed_args.image)

        activation_status = None
        if parsed_args.deactivate:
            image_client.images.deactivate(image.id)
            activation_status = "deactivated"
        if parsed_args.activate:
            image_client.images.reactivate(image.id)
            activation_status = "activated"

        membership_group_args = ('accept', 'reject', 'pending')
        membership_status = [
            status for status in membership_group_args
            if getattr(parsed_args, status)
        ]
        if membership_status:
            # If a specific project is not passed, assume we want to update
            # our own membership
            if not project_id:
                project_id = self.app.client_manager.auth_ref.project_id
            # The mutually exclusive group of the arg parser ensure we have at
            # most one item in the membership_status list.
            if membership_status[0] != 'pending':
                membership_status[0] += 'ed'  # Glance expects the past form
            image_client.image_members.update(image.id, project_id,
                                              membership_status[0])

        if parsed_args.tags:
            # Tags should be extended, but duplicates removed
            kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))

        try:
            image = image_client.images.update(image.id, **kwargs)
        except Exception:
            if activation_status is not None:
                LOG.info(_("Image %(id)s was %(status)s."), {
                    'id': image.id,
                    'status': activation_status
                })
            raise
Beispiel #8
0
    def _validate_args(self, parsed_args):
        # TODO(bcrochet): This should be removed after Rocky or 'S'.
        if any(map(lambda x: getattr(parsed_args, x) is not None, [
            'control_scale',
            'compute_scale',
            'ceph_storage_scale',
            'block_storage_scale',
            'swift_storage_scale',
            'control_flavor',
            'compute_flavor',
            'ceph_storage_flavor',
            'block_storage_flavor',
            'swift_storage_flavor'
        ])):
            raise oscexc.CommandError(
                "A scale or flavor argument was passed to the command line. "
                "These arguments are no longer valid. They MUST be replaced "
                "with an environment file that contains a valid "
                "parameter_default. Failure to do so may cause possible data "
                "loss or a decommisioning of nodes.")

        if parsed_args.templates is None and parsed_args.answers_file is None:
            raise oscexc.CommandError(
                "You must specify either --templates or --answers-file")

        if parsed_args.environment_files:
            nonexisting_envs = []
            jinja2_envs = []
            for env_file in parsed_args.environment_files:

                if env_file.endswith(".j2.yaml"):
                    jinja2_envs.append(env_file)
                elif not os.path.isfile(env_file):
                    # Tolerate missing file if there's a j2.yaml file that will
                    # be rendered in the plan but not available locally (yet)
                    if not os.path.isfile(env_file.replace(".yaml",
                                                           ".j2.yaml")):
                        nonexisting_envs.append(env_file)

            # Check networks_file existence
            if parsed_args.networks_file:
                if not os.path.isfile(parsed_args.networks_file):
                    nonexisting_envs.append(parsed_args.networks_file)

            # check plan_environment_file existence
            if parsed_args.plan_environment_file:
                if not os.path.isfile(parsed_args.plan_environment_file):
                    nonexisting_envs.append(parsed_args.plan_environment_file)

            # check answers_file existence
            if parsed_args.answers_file:
                if not os.path.isfile(parsed_args.answers_file):
                    nonexisting_envs.append(parsed_args.answers_file)

            if jinja2_envs:
                rewritten_paths = [e.replace(".j2.yaml", ".yaml")
                                   for e in jinja2_envs]
                raise oscexc.CommandError(
                    "Error: The following jinja2 files were provided: -e "
                    "{}. Did you mean -e {}?".format(
                        ' -e '.join(jinja2_envs),
                        ' -e '.join(rewritten_paths)))
            if nonexisting_envs:
                raise oscexc.CommandError(
                    "Error: The following files were not found: {0}".format(
                        ", ".join(nonexisting_envs)))

        if (parsed_args.baremetal_deployment
                and not parsed_args.deployed_server):
            raise oscexc.CommandError(
                "Error: --deployed-server must be used when using "
                "--baremetal-deployment")

        if parsed_args.deployed_server and (parsed_args.run_validations
           or not parsed_args.disable_validations):
                raise oscexc.CommandError(
                    "Error: The --deployed-server cannot be used without "
                    "the --disable-validations")

        if parsed_args.environment_directories:
            self._validate_args_environment_directory(
                parsed_args.environment_directories)
    def take_action(self, parsed_args):
        identity_client = self.app.client_manager.identity

        if parsed_args.user:
            user = common.find_user(
                identity_client,
                parsed_args.user,
                parsed_args.user_domain,
            )
        elif parsed_args.group:
            group = common.find_group(
                identity_client,
                parsed_args.group,
                parsed_args.group_domain,
            )

        if parsed_args.domain:
            domain = common.find_domain(
                identity_client,
                parsed_args.domain,
            )
        elif parsed_args.project:
            project = common.find_project(
                identity_client,
                parsed_args.project,
                parsed_args.project_domain,
            )

        # no user or group specified, list all roles in the system
        if not parsed_args.user and not parsed_args.group:
            if not parsed_args.domain:
                columns = ('ID', 'Name')
                data = identity_client.roles.list()
            else:
                columns = ('ID', 'Name', 'Domain')
                data = identity_client.roles.list(domain_id=domain.id)
                for role in data:
                    role.domain = domain.name
        elif parsed_args.user and parsed_args.domain:
            columns = ('ID', 'Name', 'Domain', 'User')
            data = identity_client.roles.list(
                user=user,
                domain=domain,
                os_inherit_extension_inherited=parsed_args.inherited)
            for user_role in data:
                user_role.user = user.name
                user_role.domain = domain.name
            self.log.warning(
                _('Listing assignments using role list is '
                  'deprecated. Use role assignment list --user '
                  '<user-name> --domain <domain-name> --names '
                  'instead.'))
        elif parsed_args.user and parsed_args.project:
            columns = ('ID', 'Name', 'Project', 'User')
            data = identity_client.roles.list(
                user=user,
                project=project,
                os_inherit_extension_inherited=parsed_args.inherited)
            for user_role in data:
                user_role.user = user.name
                user_role.project = project.name
            self.log.warning(
                _('Listing assignments using role list is '
                  'deprecated. Use role assignment list --user '
                  '<user-name> --project <project-name> --names '
                  'instead.'))
        elif parsed_args.user:
            columns = ('ID', 'Name')
            data = identity_client.roles.list(
                user=user,
                domain='default',
                os_inherit_extension_inherited=parsed_args.inherited)
            self.log.warning(
                _('Listing assignments using role list is '
                  'deprecated. Use role assignment list --user '
                  '<user-name> --domain default --names '
                  'instead.'))
        elif parsed_args.group and parsed_args.domain:
            columns = ('ID', 'Name', 'Domain', 'Group')
            data = identity_client.roles.list(
                group=group,
                domain=domain,
                os_inherit_extension_inherited=parsed_args.inherited)
            for group_role in data:
                group_role.group = group.name
                group_role.domain = domain.name
            self.log.warning(
                _('Listing assignments using role list is '
                  'deprecated. Use role assignment list --group '
                  '<group-name> --domain <domain-name> --names '
                  'instead.'))
        elif parsed_args.group and parsed_args.project:
            columns = ('ID', 'Name', 'Project', 'Group')
            data = identity_client.roles.list(
                group=group,
                project=project,
                os_inherit_extension_inherited=parsed_args.inherited)
            for group_role in data:
                group_role.group = group.name
                group_role.project = project.name
            self.log.warning(
                _('Listing assignments using role list is '
                  'deprecated. Use role assignment list --group '
                  '<group-name> --project <project-name> --names '
                  'instead.'))
        else:
            msg = _("Error: If a user or group is specified, "
                    "either --domain or --project must also be "
                    "specified to list role grants.")
            raise exceptions.CommandError(msg)

        return (columns, (utils.get_item_properties(
            s,
            columns,
            formatters={},
        ) for s in data))
Beispiel #10
0
    def take_action(self, parsed_args):
        volume_client = self.app.client_manager.volume
        volume = utils.find_resource(volume_client.volumes, parsed_args.volume)

        result = 0
        if parsed_args.size:
            try:
                if volume.status != 'available':
                    msg = (_("Volume is in %s state, it must be available "
                             "before size can be extended") % volume.status)
                    raise exceptions.CommandError(msg)
                if parsed_args.size <= volume.size:
                    msg = (_("New size must be greater than %s GB") %
                           volume.size)
                    raise exceptions.CommandError(msg)
                volume_client.volumes.extend(volume.id, parsed_args.size)
            except Exception as e:
                LOG.error(_("Failed to set volume size: %s"), e)
                result += 1

        if parsed_args.no_property:
            try:
                volume_client.volumes.delete_metadata(volume.id,
                                                      volume.metadata.keys())
            except Exception as e:
                LOG.error(_("Failed to clean volume properties: %s"), e)
                result += 1

        if parsed_args.property:
            try:
                volume_client.volumes.set_metadata(volume.id,
                                                   parsed_args.property)
            except Exception as e:
                LOG.error(_("Failed to set volume property: %s"), e)
                result += 1
        if parsed_args.bootable or parsed_args.non_bootable:
            try:
                volume_client.volumes.set_bootable(volume.id,
                                                   parsed_args.bootable)
            except Exception as e:
                LOG.error(_("Failed to set volume bootable property: %s"), e)
                result += 1
        if parsed_args.read_only or parsed_args.read_write:
            try:
                volume_client.volumes.update_readonly_flag(
                    volume.id, parsed_args.read_only)
            except Exception as e:
                LOG.error(
                    _("Failed to set volume read-only access "
                      "mode flag: %s"), e)
                result += 1
        kwargs = {}
        if parsed_args.name:
            kwargs['display_name'] = parsed_args.name
        if parsed_args.description:
            kwargs['display_description'] = parsed_args.description
        if kwargs:
            try:
                volume_client.volumes.update(volume.id, **kwargs)
            except Exception as e:
                LOG.error(
                    _("Failed to update volume display name "
                      "or display description: %s"), e)
                result += 1

        if result > 0:
            raise exceptions.CommandError(
                _("One or more of the "
                  "set operations failed"))
Beispiel #11
0
    def take_action(self, parsed_args):
        def _deprecated():
            # NOTE(henry-nash): Deprecated as of Newton, so we should remove
            # this in the 'P' release.
            self.log.warning(
                _('Listing assignments using role list is '
                  'deprecated as of the Newton release. Use role '
                  'assignment list --user <user-name> --project '
                  '<project-name> --names instead.'))

        identity_client = self.app.client_manager.identity
        auth_ref = self.app.client_manager.auth_ref

        # No user or project specified, list all roles in the system
        if not parsed_args.user and not parsed_args.project:
            columns = ('ID', 'Name')
            data = identity_client.roles.list()
        elif parsed_args.user and parsed_args.project:
            user = utils.find_resource(
                identity_client.users,
                parsed_args.user,
            )
            project = utils.find_resource(
                identity_client.projects,
                parsed_args.project,
            )
            _deprecated()
            data = identity_client.roles.roles_for_user(user.id, project.id)

        elif parsed_args.user:
            user = utils.find_resource(
                identity_client.users,
                parsed_args.user,
            )
            if self.app.client_manager.auth_ref:
                project = utils.find_resource(identity_client.projects,
                                              auth_ref.project_id)
            else:
                msg = _("Project must be specified")
                raise exceptions.CommandError(msg)
            _deprecated()
            data = identity_client.roles.roles_for_user(user.id, project.id)
        elif parsed_args.project:
            project = utils.find_resource(
                identity_client.projects,
                parsed_args.project,
            )
            if self.app.client_manager.auth_ref:
                user = utils.find_resource(identity_client.users,
                                           auth_ref.user_id)
            else:
                msg = _("User must be specified")
                raise exceptions.CommandError(msg)
            _deprecated()
            data = identity_client.roles.roles_for_user(user.id, project.id)

        if parsed_args.user or parsed_args.project:
            columns = ('ID', 'Name', 'Project', 'User')
            for user_role in data:
                user_role.user = user.name
                user_role.project = project.name

        return (columns, (utils.get_item_properties(
            s,
            columns,
            formatters={},
        ) for s in data))
def _get_attrs(client_manager, parsed_args, is_create=True):
    attrs = {}
    if 'name' in parsed_args and parsed_args.name is not None:
        attrs['name'] = str(parsed_args.name)

    if is_create:
        if 'project' in parsed_args and parsed_args.project is not None:
            identity_client = client_manager.identity
            project_id = identity_common.find_project(
                identity_client,
                parsed_args.project,
                parsed_args.project_domain,
            ).id
            attrs['tenant_id'] = project_id
        client = client_manager.network
        attrs['network_id'] = client.find_network(parsed_args.network,
                                                  ignore_missing=False).id
        if parsed_args.subnet_pool is not None:
            subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool,
                                                  ignore_missing=False)
            attrs['subnetpool_id'] = subnet_pool.id
        if parsed_args.use_default_subnet_pool:
            attrs['use_default_subnet_pool'] = True
        if parsed_args.prefix_length is not None:
            attrs['prefixlen'] = parsed_args.prefix_length
        if parsed_args.subnet_range is not None:
            attrs['cidr'] = parsed_args.subnet_range
        if parsed_args.ip_version is not None:
            attrs['ip_version'] = parsed_args.ip_version
        if parsed_args.ipv6_ra_mode is not None:
            attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
        if parsed_args.ipv6_address_mode is not None:
            attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
        if parsed_args.network_segment is not None:
            attrs['segment_id'] = client.find_segment(
                parsed_args.network_segment, ignore_missing=False).id

    if 'gateway' in parsed_args and parsed_args.gateway is not None:
        gateway = parsed_args.gateway.lower()

        if not is_create and gateway == 'auto':
            msg = _("Auto option is not available for Subnet Set. "
                    "Valid options are <ip-address> or none")
            raise exceptions.CommandError(msg)
        elif gateway != 'auto':
            if gateway == 'none':
                attrs['gateway_ip'] = None
            else:
                attrs['gateway_ip'] = gateway
    if ('allocation_pools' in parsed_args
            and parsed_args.allocation_pools is not None):
        attrs['allocation_pools'] = parsed_args.allocation_pools
    if parsed_args.dhcp:
        attrs['enable_dhcp'] = True
    if parsed_args.no_dhcp:
        attrs['enable_dhcp'] = False
    if ('dns_nameservers' in parsed_args
            and parsed_args.dns_nameservers is not None):
        attrs['dns_nameservers'] = parsed_args.dns_nameservers
    if 'host_routes' in parsed_args and parsed_args.host_routes is not None:
        # Change 'gateway' entry to 'nexthop' to match the API
        attrs['host_routes'] = convert_entries_to_nexthop(
            parsed_args.host_routes)
    if ('service_types' in parsed_args
            and parsed_args.service_types is not None):
        attrs['service_types'] = parsed_args.service_types
    if parsed_args.description is not None:
        attrs['description'] = parsed_args.description

    # wrs extensions
    if ('vlan_id' in parsed_args and parsed_args.vlan_id is not None):
        attrs['wrs-net:vlan_id'] = parsed_args.vlan_id
    if ('network_type' in parsed_args
            and parsed_args.network_type is not None):
        attrs['wrs-provider:network_type'] = parsed_args.network_type
    if ('physical_network' in parsed_args
            and parsed_args.physical_network is not None):
        attrs['wrs-provider:vlan_id'] = parsed_args.physical_network
    if ('segmentation_id' in parsed_args
            and parsed_args.segmentation_id is not None):
        attrs['wrs-provider:segmentation_id'] = parsed_args.segmentation_id
    if ('unmanaged' in parsed_args and parsed_args.unmanaged is not False):
        attrs['wrs-net:managed'] = False

    return attrs
Beispiel #13
0
 def take_action_compute(self, client, parsed_args):
     raise exceptions.CommandError("This command needs access to"
                                   " a network endpoint.")
     return
Beispiel #14
0
    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)", parsed_args)
        senlin_client = self.app.client_manager.clustering

        action_args = {}

        capacity = parsed_args.capacity
        adjustment = parsed_args.adjustment
        percentage = parsed_args.percentage
        min_size = parsed_args.min_size
        max_size = parsed_args.max_size
        min_step = parsed_args.min_step

        if sum(v is not None for v in (capacity, adjustment, percentage)) > 1:
            raise exc.CommandError(_("Only one of 'capacity', 'adjustment' and"
                                     " 'percentage' can be specified."))

        action_args['adjustment_type'] = None
        action_args['number'] = None

        if capacity is not None:
            if capacity < 0:
                raise exc.CommandError(_('Cluster capacity must be larger than'
                                         ' or equal to zero.'))
            action_args['adjustment_type'] = 'EXACT_CAPACITY'
            action_args['number'] = capacity

        if adjustment is not None:
            if adjustment == 0:
                raise exc.CommandError(_('Adjustment cannot be zero.'))
            action_args['adjustment_type'] = 'CHANGE_IN_CAPACITY'
            action_args['number'] = adjustment

        if percentage is not None:
            if (percentage == 0 or percentage == 0.0):
                raise exc.CommandError(_('Percentage cannot be zero.'))
            action_args['adjustment_type'] = 'CHANGE_IN_PERCENTAGE'
            action_args['number'] = percentage

        if min_step is not None:
            if percentage is None:
                raise exc.CommandError(_('Min step is only used with '
                                         'percentage.'))

        if min_size is not None:
            if min_size < 0:
                raise exc.CommandError(_('Min size cannot be less than zero.'))
            if max_size is not None and max_size >= 0 and min_size > max_size:
                raise exc.CommandError(_('Min size cannot be larger than '
                                         'max size.'))
            if capacity is not None and min_size > capacity:
                raise exc.CommandError(_('Min size cannot be larger than the '
                                         'specified capacity'))

        if max_size is not None:
            if capacity is not None and max_size > 0 and max_size < capacity:
                raise exc.CommandError(_('Max size cannot be less than the '
                                         'specified capacity.'))
            # do a normalization
            if max_size < 0:
                max_size = -1

        action_args['min_size'] = min_size
        action_args['max_size'] = max_size
        action_args['min_step'] = min_step
        action_args['strict'] = parsed_args.strict

        resp = senlin_client.cluster_resize(parsed_args.cluster, **action_args)
        print('Request accepted by action: %s' % resp['action'])
Beispiel #15
0
    def take_action(self, parsed_args):
        self.log.debug("take_action(%s)", parsed_args)

        heat_client = self.app.client_manager.orchestration

        try:
            if not parsed_args.yes and sys.stdin.isatty():
                sys.stdout.write(
                    _("Are you sure you want to delete this stack(s) [y/N]? "))
                prompt_response = sys.stdin.readline().lower()
                if not prompt_response.startswith('y'):
                    self.log.info(_LI('User did not confirm stack delete so '
                                      'taking no action.'))
                    return
        except KeyboardInterrupt:  # ctrl-c
            self.log.info(_LI('User did not confirm stack delete '
                              '(ctrl-c) so taking no action.'))
            return
        except EOFError:  # ctrl-d
            self.log.info(_LI('User did not confirm stack delete '
                              '(ctrl-d) so taking no action.'))
            return

        failure_count = 0
        stacks_waiting = []
        for sid in parsed_args.stack:
            marker = None
            if parsed_args.wait:
                try:
                    # find the last event to use as the marker
                    events = event_utils.get_events(heat_client,
                                                    stack_id=sid,
                                                    event_args={
                                                        'sort_dir': 'desc'},
                                                    limit=1)
                    if events:
                        marker = events[0].id
                except heat_exc.CommandError as ex:
                    failure_count += 1
                    print(ex)
                    continue

            try:
                heat_client.stacks.delete(sid)
                stacks_waiting.append((sid, marker))
            except heat_exc.HTTPNotFound:
                failure_count += 1
                print(_('Stack not found: %s') % sid)
            except heat_exc.Forbidden:
                failure_count += 1
                print(_('Forbidden: %s') % sid)

        if parsed_args.wait:
            for sid, marker in stacks_waiting:
                try:
                    stack_status, msg = event_utils.poll_for_events(
                        heat_client, sid, action='DELETE', marker=marker)
                except heat_exc.CommandError:
                    continue
                if stack_status == 'DELETE_FAILED':
                    failure_count += 1
                    print(msg)

        if failure_count:
            msg = (_('Unable to delete %(count)d of the %(total)d stacks.') %
                   {'count': failure_count, 'total': len(parsed_args.stack)})
            raise exc.CommandError(msg)
Beispiel #16
0
    def take_action(self, parsed_args):
        identity_client = self.app.client_manager.identity
        image_client = self.app.client_manager.image

        for deadopt in self.deadopts:
            if getattr(parsed_args, deadopt.replace('-', '_'), None):
                raise exceptions.CommandError(
                    _("ERROR: --%s was given, which is an Image v1 option"
                      " that is no longer supported in Image v2") % deadopt)

        image = image_client.find_image(
            parsed_args.image,
            ignore_missing=False,
        )
        project_id = None
        if parsed_args.project:
            project_id = common.find_project(
                identity_client,
                parsed_args.project,
                parsed_args.project_domain,
            ).id

        # handle activation status changes

        activation_status = None
        if parsed_args.deactivate or parsed_args.activate:
            if parsed_args.deactivate:
                image_client.deactivate_image(image.id)
                activation_status = "deactivated"
            if parsed_args.activate:
                image_client.reactivate_image(image.id)
                activation_status = "activated"

        # handle membership changes

        if parsed_args.membership:
            # If a specific project is not passed, assume we want to update
            # our own membership
            if not project_id:
                project_id = self.app.client_manager.auth_ref.project_id
            image_client.update_member(
                image=image.id,
                member=project_id,
                status=parsed_args.membership,
            )

        # handle everything else

        kwargs = {}
        copy_attrs = ('architecture', 'container_format', 'disk_format',
                      'file', 'instance_id', 'kernel_id', 'locations',
                      'min_disk', 'min_ram', 'name', 'os_distro', 'os_version',
                      'prefix', 'progress', 'ramdisk_id', 'tags', 'visibility')
        for attr in copy_attrs:
            if attr in parsed_args:
                val = getattr(parsed_args, attr, None)
                if val is not None:
                    # Only include a value in kwargs for attributes that are
                    # actually present on the command line
                    kwargs[attr] = val

        # Properties should get flattened into the general kwargs
        if getattr(parsed_args, 'properties', None):
            for k, v in parsed_args.properties.items():
                kwargs[k] = str(v)

        # Handle exclusive booleans with care
        # Avoid including attributes in kwargs if an option is not
        # present on the command line.  These exclusive booleans are not
        # a single value for the pair of options because the default must be
        # to do nothing when no options are present as opposed to always
        # setting a default.
        if parsed_args.protected:
            kwargs['is_protected'] = True
        if parsed_args.unprotected:
            kwargs['is_protected'] = False
        if parsed_args.public:
            kwargs['visibility'] = 'public'
        if parsed_args.private:
            kwargs['visibility'] = 'private'
        if parsed_args.community:
            kwargs['visibility'] = 'community'
        if parsed_args.shared:
            kwargs['visibility'] = 'shared'
        if parsed_args.project:
            # We already did the project lookup above
            kwargs['owner_id'] = project_id
        if parsed_args.tags:
            # Tags should be extended, but duplicates removed
            kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags)))
        if parsed_args.hidden is not None:
            kwargs['is_hidden'] = parsed_args.hidden

        try:
            image = image_client.update_image(image.id, **kwargs)
        except Exception:
            if activation_status is not None:
                LOG.info(_("Image %(id)s was %(status)s."), {
                    'id': image.id,
                    'status': activation_status
                })
            raise
    def take_action_network(self, client, parsed_args):
        # Get the security group ID to hold the rule.
        security_group_id = client.find_security_group(
            parsed_args.group,
            ignore_missing=False
        ).id

        # Build the create attributes.
        attrs = {}
        attrs['protocol'] = self._get_protocol(parsed_args)

        if parsed_args.description is not None:
            attrs['description'] = parsed_args.description

        # NOTE(rtheis): A direction must be specified and ingress
        # is the default.
        if parsed_args.ingress or not parsed_args.egress:
            attrs['direction'] = 'ingress'
        if parsed_args.egress:
            attrs['direction'] = 'egress'

        # NOTE(rtheis): Use ethertype specified else default based
        # on IP protocol.
        if parsed_args.ethertype:
            attrs['ethertype'] = parsed_args.ethertype
        elif self._is_ipv6_protocol(attrs['protocol']):
            attrs['ethertype'] = 'IPv6'
        else:
            attrs['ethertype'] = 'IPv4'

        # NOTE(rtheis): Validate the port range and ICMP type and code.
        # It would be ideal if argparse could do this.
        if parsed_args.dst_port and (parsed_args.icmp_type or
                                     parsed_args.icmp_code):
            msg = _('Argument --dst-port not allowed with arguments '
                    '--icmp-type and --icmp-code')
            raise exceptions.CommandError(msg)
        if parsed_args.icmp_type is None and parsed_args.icmp_code is not None:
            msg = _('Argument --icmp-type required with argument --icmp-code')
            raise exceptions.CommandError(msg)
        is_icmp_protocol = _is_icmp_protocol(attrs['protocol'])
        if not is_icmp_protocol and (parsed_args.icmp_type or
                                     parsed_args.icmp_code):
            msg = _('ICMP IP protocol required with arguments '
                    '--icmp-type and --icmp-code')
            raise exceptions.CommandError(msg)
        # NOTE(rtheis): For backwards compatibility, continue ignoring
        # the destination port range when an ICMP IP protocol is specified.
        if parsed_args.dst_port and not is_icmp_protocol:
            attrs['port_range_min'] = parsed_args.dst_port[0]
            attrs['port_range_max'] = parsed_args.dst_port[1]
        if parsed_args.icmp_type:
            attrs['port_range_min'] = parsed_args.icmp_type
        if parsed_args.icmp_code:
            attrs['port_range_max'] = parsed_args.icmp_code

        # NOTE(dtroyer): --src-ip and --src-group were deprecated in Nov 2016.
        #                Do not remove before 4.x release or Nov 2017.
        if not (parsed_args.remote_group is None and
                parsed_args.src_group is None):
            attrs['remote_group_id'] = client.find_security_group(
                parsed_args.remote_group or parsed_args.src_group,
                ignore_missing=False
            ).id
            if parsed_args.src_group:
                LOG.warning(
                    _("The %(old)s option is deprecated, "
                      "please use %(new)s instead."),
                    {'old': '--src-group', 'new': '--remote-group'},
                )
        elif not (parsed_args.remote_ip is None and
                  parsed_args.src_ip is None):
            attrs['remote_ip_prefix'] = (
                parsed_args.remote_ip or parsed_args.src_ip
            )
            if parsed_args.src_ip:
                LOG.warning(
                    _("The %(old)s option is deprecated, "
                      "please use %(new)s instead."),
                    {'old': '--src-ip', 'new': '--remote-ip'},
                )
        elif attrs['ethertype'] == 'IPv4':
            attrs['remote_ip_prefix'] = '0.0.0.0/0'
        attrs['security_group_id'] = security_group_id
        if parsed_args.project is not None:
            identity_client = self.app.client_manager.identity
            project_id = identity_common.find_project(
                identity_client,
                parsed_args.project,
                parsed_args.project_domain,
            ).id
            attrs['tenant_id'] = project_id

        # Create and show the security group rule.
        obj = client.create_security_group_rule(**attrs)
        display_columns, columns = _get_columns(obj)
        data = utils.get_item_properties(obj, columns)
        return (display_columns, data)
Beispiel #18
0
    def take_action(self, parsed_args):
        identity_client = self.app.client_manager.identity
        image_client = self.app.client_manager.image

        for deadopt in self.deadopts:
            if getattr(parsed_args, deadopt.replace('-', '_'), None):
                raise exceptions.CommandError(
                    _("ERROR: --%s was given, which is an Image v1 option"
                      " that is no longer supported in Image v2") % deadopt)

        # Build an attribute dict from the parsed args, only include
        # attributes that were actually set on the command line
        kwargs = {'allow_duplicates': True}
        copy_attrs = ('name', 'id', 'container_format', 'disk_format',
                      'min_disk', 'min_ram', 'tags', 'visibility')
        for attr in copy_attrs:
            if attr in parsed_args:
                val = getattr(parsed_args, attr, None)
                if val:
                    # Only include a value in kwargs for attributes that
                    # are actually present on the command line
                    kwargs[attr] = val

        # properties should get flattened into the general kwargs
        if getattr(parsed_args, 'properties', None):
            for k, v in parsed_args.properties.items():
                kwargs[k] = str(v)

        # Handle exclusive booleans with care
        # Avoid including attributes in kwargs if an option is not
        # present on the command line.  These exclusive booleans are not
        # a single value for the pair of options because the default must be
        # to do nothing when no options are present as opposed to always
        # setting a default.
        if parsed_args.protected:
            kwargs['is_protected'] = True
        if parsed_args.unprotected:
            kwargs['is_protected'] = False
        if parsed_args.public:
            kwargs['visibility'] = 'public'
        if parsed_args.private:
            kwargs['visibility'] = 'private'
        if parsed_args.community:
            kwargs['visibility'] = 'community'
        if parsed_args.shared:
            kwargs['visibility'] = 'shared'
        if parsed_args.project:
            kwargs['owner_id'] = common.find_project(
                identity_client,
                parsed_args.project,
                parsed_args.project_domain,
            ).id

        if parsed_args.use_import:
            kwargs['use_import'] = True

        # open the file first to ensure any failures are handled before the
        # image is created. Get the file name (if it is file, and not stdin)
        # for easier further handling.
        (fp, fname) = get_data_file(parsed_args)
        info = {}

        if fp is not None and parsed_args.volume:
            raise exceptions.CommandError(
                _("Uploading data and using "
                  "container are not allowed at "
                  "the same time"))
        if fp is None and parsed_args.file:
            LOG.warning(_("Failed to get an image file."))
            return {}, {}
        if fp is not None and parsed_args.progress:
            filesize = os.path.getsize(fname)
            if filesize is not None:
                kwargs['validate_checksum'] = False
                kwargs['data'] = progressbar.VerboseFileWrapper(fp, filesize)
        elif fname:
            kwargs['filename'] = fname
        elif fp:
            kwargs['validate_checksum'] = False
            kwargs['data'] = fp

        # sign an image using a given local private key file
        if parsed_args.sign_key_path or parsed_args.sign_cert_id:
            if not parsed_args.file:
                msg = (_("signing an image requires the --file option, "
                         "passing files via stdin when signing is not "
                         "supported."))
                raise exceptions.CommandError(msg)
            if (len(parsed_args.sign_key_path) < 1
                    or len(parsed_args.sign_cert_id) < 1):
                msg = (_("'sign-key-path' and 'sign-cert-id' must both be "
                         "specified when attempting to sign an image."))
                raise exceptions.CommandError(msg)
            else:
                sign_key_path = parsed_args.sign_key_path
                sign_cert_id = parsed_args.sign_cert_id
                signer = image_signer.ImageSigner()
                try:
                    pw = utils.get_password(
                        self.app.stdin,
                        prompt=("Please enter private key password, leave "
                                "empty if none: "),
                        confirm=False)

                    if not pw or len(pw) < 1:
                        pw = None
                    else:
                        # load_private_key() requires the password to be
                        # passed as bytes
                        pw = pw.encode()

                    signer.load_private_key(sign_key_path, password=pw)
                except Exception:
                    msg = (_("Error during sign operation: private key "
                             "could not be loaded."))
                    raise exceptions.CommandError(msg)

                signature = signer.generate_signature(fp)
                signature_b64 = b64encode(signature)
                kwargs['img_signature'] = signature_b64
                kwargs['img_signature_certificate_uuid'] = sign_cert_id
                kwargs['img_signature_hash_method'] = signer.hash_method
                if signer.padding_method:
                    kwargs['img_signature_key_type'] = \
                        signer.padding_method

        # If a volume is specified.
        if parsed_args.volume:
            volume_client = self.app.client_manager.volume
            source_volume = utils.find_resource(
                volume_client.volumes,
                parsed_args.volume,
            )
            response, body = volume_client.volumes.upload_to_image(
                source_volume.id,
                parsed_args.force,
                parsed_args.name,
                parsed_args.container_format,
                parsed_args.disk_format,
                visibility=kwargs.get('visibility', 'private'),
                protected=True if parsed_args.protected else False)
            info = body['os-volume_upload_image']
            try:
                info['volume_type'] = info['volume_type']['name']
            except TypeError:
                info['volume_type'] = None
        else:
            image = image_client.create_image(**kwargs)

        if not info:
            info = _format_image(image)

        return zip(*sorted(info.items()))
    def take_action(self, parsed_args):
        identity_client = self.app.client_manager.identity
        image_client = self.app.client_manager.image

        for deadopt in self.deadopts:
            if getattr(parsed_args, deadopt.replace('-', '_'), None):
                raise exceptions.CommandError(
                    _("ERROR: --%s was given, which is an Image v1 option"
                      " that is no longer supported in Image v2") % deadopt)

        # Build an attribute dict from the parsed args, only include
        # attributes that were actually set on the command line
        kwargs = {}
        copy_attrs = ('name', 'id', 'container_format', 'disk_format',
                      'min_disk', 'min_ram', 'tags', 'visibility')
        for attr in copy_attrs:
            if attr in parsed_args:
                val = getattr(parsed_args, attr, None)
                if val:
                    # Only include a value in kwargs for attributes that
                    # are actually present on the command line
                    kwargs[attr] = val

        # properties should get flattened into the general kwargs
        if getattr(parsed_args, 'properties', None):
            for k, v in six.iteritems(parsed_args.properties):
                kwargs[k] = str(v)

        # Handle exclusive booleans with care
        # Avoid including attributes in kwargs if an option is not
        # present on the command line.  These exclusive booleans are not
        # a single value for the pair of options because the default must be
        # to do nothing when no options are present as opposed to always
        # setting a default.
        if parsed_args.protected:
            kwargs['protected'] = True
        if parsed_args.unprotected:
            kwargs['protected'] = False
        if parsed_args.public:
            kwargs['visibility'] = 'public'
        if parsed_args.private:
            kwargs['visibility'] = 'private'
        if parsed_args.community:
            kwargs['visibility'] = 'community'
        if parsed_args.shared:
            kwargs['visibility'] = 'shared'
        # Handle deprecated --owner option
        project_arg = parsed_args.project
        if parsed_args.owner:
            project_arg = parsed_args.owner
            LOG.warning(
                _('The --owner option is deprecated, '
                  'please use --project instead.'))
        if project_arg:
            kwargs['owner'] = common.find_project(
                identity_client,
                project_arg,
                parsed_args.project_domain,
            ).id

        # open the file first to ensure any failures are handled before the
        # image is created
        fp = gc_utils.get_data_file(parsed_args)
        info = {}
        if fp is not None and parsed_args.volume:
            raise exceptions.CommandError(
                _("Uploading data and using "
                  "container are not allowed at "
                  "the same time"))

        if fp is None and parsed_args.file:
            LOG.warning(_("Failed to get an image file."))
            return {}, {}

        if parsed_args.owner:
            kwargs['owner'] = common.find_project(
                identity_client,
                parsed_args.owner,
                parsed_args.project_domain,
            ).id

        # sign an image using a given local private key file
        if parsed_args.sign_key_path or parsed_args.sign_cert_id:
            if not parsed_args.file:
                msg = (_("signing an image requires the --file option, "
                         "passing files via stdin when signing is not "
                         "supported."))
                raise exceptions.CommandError(msg)
            if (len(parsed_args.sign_key_path) < 1
                    or len(parsed_args.sign_cert_id) < 1):
                msg = (_("'sign-key-path' and 'sign-cert-id' must both be "
                         "specified when attempting to sign an image."))
                raise exceptions.CommandError(msg)
            else:
                sign_key_path = parsed_args.sign_key_path
                sign_cert_id = parsed_args.sign_cert_id
                signer = image_signer.ImageSigner()
                try:
                    pw = utils.get_password(
                        self.app.stdin,
                        prompt=("Please enter private key password, leave "
                                "empty if none: "),
                        confirm=False)
                    if not pw or len(pw) < 1:
                        pw = None
                    signer.load_private_key(sign_key_path, password=pw)
                except Exception:
                    msg = (_("Error during sign operation: private key could "
                             "not be loaded."))
                    raise exceptions.CommandError(msg)

                signature = signer.generate_signature(fp)
                signature_b64 = b64encode(signature)
                kwargs['img_signature'] = signature_b64
                kwargs['img_signature_certificate_uuid'] = sign_cert_id
                kwargs['img_signature_hash_method'] = signer.hash_method
                if signer.padding_method:
                    kwargs['img_signature_key_type'] = signer.padding_method

        # If a volume is specified.
        if parsed_args.volume:
            volume_client = self.app.client_manager.volume
            source_volume = utils.find_resource(
                volume_client.volumes,
                parsed_args.volume,
            )
            response, body = volume_client.volumes.upload_to_image(
                source_volume.id,
                parsed_args.force,
                parsed_args.name,
                parsed_args.container_format,
                parsed_args.disk_format,
            )
            info = body['os-volume_upload_image']
            try:
                info['volume_type'] = info['volume_type']['name']
            except TypeError:
                info['volume_type'] = None
        else:
            image = image_client.images.create(**kwargs)

        if fp is not None:
            with fp:
                try:
                    image_client.images.upload(image.id, fp)
                except Exception:
                    # If the upload fails for some reason attempt to remove the
                    # dangling queued image made by the create() call above but
                    # only if the user did not specify an id which indicates
                    # the Image already exists and should be left alone.
                    try:
                        if 'id' not in kwargs:
                            image_client.images.delete(image.id)
                    except Exception:
                        pass  # we don't care about this one
                    raise  # now, throw the upload exception again

                # update the image after the data has been uploaded
                image = image_client.images.get(image.id)

        if not info:
            info = _format_image(image)

        return zip(*sorted(six.iteritems(info)))
Beispiel #20
0
    def take_action(self, parsed_args):
        def _show_progress(progress):
            if progress:
                self.app.stderr.write('\rProgress: %s' % progress)
                self.app.stderr.flush()

        compute_client = self.app.client_manager.compute

        server = utils.find_resource(
            compute_client.servers,
            parsed_args.server,
        )

        # Set sane defaults as this API wants all mouths to be fed
        if parsed_args.name is None:
            backup_name = server.name
        else:
            backup_name = parsed_args.name
        if parsed_args.type is None:
            backup_type = ""
        else:
            backup_type = parsed_args.type
        if parsed_args.rotate is None:
            backup_rotation = 1
        else:
            backup_rotation = parsed_args.rotate

        compute_client.servers.backup(
            server.id,
            backup_name,
            backup_type,
            backup_rotation,
        )

        image_client = self.app.client_manager.image
        image = utils.find_resource(
            image_client.images,
            backup_name,
        )

        if parsed_args.wait:
            if utils.wait_for_status(
                    image_client.images.get,
                    image.id,
                    callback=_show_progress,
            ):
                self.app.stdout.write('\n')
            else:
                msg = _('Error creating server backup: %s') % parsed_args.name
                raise exceptions.CommandError(msg)

        if self.app.client_manager._api_version['image'] == '1':
            info = {}
            info.update(image._info)
            info['properties'] = utils.format_dict(info.get('properties', {}))
        else:
            # Get the right image module to format the output
            image_module = importutils.import_module(self.IMAGE_API_VERSIONS[
                self.app.client_manager._api_version['image']])
            info = image_module._format_image(image)
        return zip(*sorted(info.items()))
Beispiel #21
0
def find_resource(manager, name_or_id, **kwargs):
    """Helper for the _find_* methods.

    :param manager: A client manager class
    :param name_or_id: The resource we are trying to find
    :param kwargs: To be used in calling .find()
    :rtype: The found resource

    This method will attempt to find a resource in a variety of ways.
    Primarily .get() methods will be called with `name_or_id` as an integer
    value, and tried again as a string value.

    If both fail, then a .find() is attempted, which is essentially calling
    a .list() function with a 'name' query parameter that is set to
    `name_or_id`.

    Lastly, if any kwargs are passed in, they will be treated as additional
    query parameters. This is particularly handy in the case of finding
    resources in a domain.

    """

    # Case 1: name_or_id is an ID, we need to call get() directly
    # for example: /projects/454ad1c743e24edcad846d1118837cac
    # For some projects, the name only will work. For keystone, this is not
    # enough information, and domain information is necessary.
    try:
        return manager.get(name_or_id)
    except Exception:
        pass

    if kwargs:
        # Case 2: name_or_id is a name, but we have query args in kwargs
        # for example: /projects/demo&domain_id=30524568d64447fbb3fa8b7891c10dd
        try:
            return manager.get(name_or_id, **kwargs)
        except Exception:
            pass

    # Case 3: Try to get entity as integer id. Keystone does not have integer
    # IDs, they are UUIDs, but some things in nova do, like flavors.
    try:
        if isinstance(name_or_id, int) or name_or_id.isdigit():
            return manager.get(int(name_or_id), **kwargs)
    # FIXME(dtroyer): The exception to catch here is dependent on which
    #                 client library the manager passed in belongs to.
    #                 Eventually this should be pulled from a common set
    #                 of client exceptions.
    except Exception as ex:
        if (type(ex).__name__ == 'NotFound'
                or type(ex).__name__ == 'HTTPNotFound'
                or type(ex).__name__ == 'TypeError'):
            pass
        else:
            raise

    # Case 4: Try to use find.
    # Reset the kwargs here for find
    if len(kwargs) == 0:
        kwargs = {}

    try:
        # Prepare the kwargs for calling find
        if 'NAME_ATTR' in manager.resource_class.__dict__:
            # novaclient does this for oddball resources
            kwargs[manager.resource_class.NAME_ATTR] = name_or_id
        else:
            kwargs['name'] = name_or_id
    except Exception:
        pass

    # finally try to find entity by name
    try:
        return manager.find(**kwargs)
    # FIXME(dtroyer): The exception to catch here is dependent on which
    #                 client library the manager passed in belongs to.
    #                 Eventually this should be pulled from a common set
    #                 of client exceptions.
    except Exception as ex:
        if type(ex).__name__ == 'NotFound':
            msg = _("No %(resource)s with a name or ID of '%(id)s' exists.")
            raise exceptions.CommandError(
                msg % {
                    'resource': manager.resource_class.__name__.lower(),
                    'id': name_or_id,
                })
        if type(ex).__name__ == 'NoUniqueMatch':
            msg = _(
                "More than one %(resource)s exists with the name '%(id)s'.")
            raise exceptions.CommandError(
                msg % {
                    'resource': manager.resource_class.__name__.lower(),
                    'id': name_or_id,
                })

        if type(ex).__name__ == 'Forbidden':
            msg = _("You are not authorized to find %(resource)s with the "
                    "name '%(id)s'.")
            raise exceptions.CommandError(
                msg % {
                    'resource': manager.resource_class.__name__.lower(),
                    'id': name_or_id,
                })
        else:
            pass

    # Case 5: For client with no find function, list all resources and hope
    # to find a matching name or ID.
    count = 0
    for resource in manager.list():
        if (resource.get('id') == name_or_id
                or resource.get('name') == name_or_id):
            count += 1
            _resource = resource
    if count == 0:
        # we found no match, report back this error:
        msg = _("Could not find resource %s")
        raise exceptions.CommandError(msg % name_or_id)
    elif count == 1:
        return _resource
    else:
        # we found multiple matches, report back this error
        msg = _("More than one resource exists with the name or ID '%s'.")
        raise exceptions.CommandError(msg % name_or_id)
Beispiel #22
0
def _get_required_firewall_rule(client, parsed_args):
    if not parsed_args.firewall_rule:
        msg = (_("Firewall rule (name or ID) is required."))
        raise exceptions.CommandError(msg)
    return client.find_resource(
        const.FWR, parsed_args.firewall_rule, cmd_resource=const.CMD_FWR)['id']
Beispiel #23
0
    def take_action(self, parsed_args):
        client = self.app.client_manager.network
        obj = client.find_router(parsed_args.router, ignore_missing=False)

        # Get the common attributes.
        attrs = _get_attrs(self.app.client_manager, parsed_args)

        # Get the route attributes.
        if parsed_args.ha:
            attrs['ha'] = True
        elif parsed_args.no_ha:
            attrs['ha'] = False
        if parsed_args.clear_routes:
            LOG.warning(
                _('The --clear-routes option is deprecated, '
                  'please use --no-route instead.'))

        if parsed_args.routes is not None:
            for route in parsed_args.routes:
                route['nexthop'] = route.pop('gateway')
            attrs['routes'] = parsed_args.routes
            if not (parsed_args.no_route or parsed_args.clear_routes):
                # Map the route keys and append to the current routes.
                # The REST API will handle route validation and duplicates.
                attrs['routes'] += obj.routes
        elif parsed_args.no_route or parsed_args.clear_routes:
            attrs['routes'] = []
        if (parsed_args.disable_snat or parsed_args.enable_snat
                or parsed_args.fixed_ip) and not parsed_args.external_gateway:
            msg = (_("You must specify '--external-gateway' in order "
                     "to update the SNAT or fixed-ip values"))
            raise exceptions.CommandError(msg)
        if parsed_args.external_gateway:
            gateway_info = {}
            network = client.find_network(parsed_args.external_gateway,
                                          ignore_missing=False)
            gateway_info['network_id'] = network.id
            if parsed_args.disable_snat:
                gateway_info['enable_snat'] = False
            if parsed_args.enable_snat:
                gateway_info['enable_snat'] = True
            if parsed_args.fixed_ip:
                ips = []
                for ip_spec in parsed_args.fixed_ip:
                    if ip_spec.get('subnet', False):
                        subnet_name_id = ip_spec.pop('subnet')
                        if subnet_name_id:
                            subnet = client.find_subnet(subnet_name_id,
                                                        ignore_missing=False)
                            ip_spec['subnet_id'] = subnet.id
                    if ip_spec.get('ip-address', False):
                        ip_spec['ip_address'] = ip_spec.pop('ip-address')
                    ips.append(ip_spec)
                gateway_info['external_fixed_ips'] = ips
            attrs['external_gateway_info'] = gateway_info

        if ((parsed_args.qos_policy or parsed_args.no_qos_policy)
                and not parsed_args.external_gateway):
            try:
                original_net_id = obj.external_gateway_info['network_id']
            except (KeyError, TypeError):
                msg = (_("You must specify '--external-gateway' or the router "
                         "must already have an external network in order to "
                         "set router gateway IP QoS"))
                raise exceptions.CommandError(msg)
            else:
                if not attrs.get('external_gateway_info'):
                    attrs['external_gateway_info'] = {}
                attrs['external_gateway_info']['network_id'] = original_net_id
        if parsed_args.qos_policy:
            check_qos_id = client.find_qos_policy(parsed_args.qos_policy,
                                                  ignore_missing=False).id
            attrs['external_gateway_info']['qos_policy_id'] = check_qos_id

        if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy:
            attrs['external_gateway_info']['qos_policy_id'] = None
        if attrs:
            client.update_router(obj, **attrs)
        # tags is a subresource and it needs to be updated separately.
        _tag.update_tags_for_set(client, obj, parsed_args)
Beispiel #24
0
    def find_attr(
        self,
        path,
        value=None,
        attr=None,
        resource=None,
    ):
        """Find a resource via attribute or ID

        Most APIs return a list wrapped by a dict with the resource
        name as key.  Some APIs (Identity) return a dict when a query
        string is present and there is one return value.  Take steps to
        unwrap these bodies and return a single dict without any resource
        wrappers.

        :param string path:
            The API-specific portion of the URL path
        :param string value:
            value to search for
        :param string attr:
            attribute to use for resource search
        :param string resource:
            plural of the object resource name; defaults to path

        For example:
            n = find(netclient, 'network', 'networks', 'matrix')
        """

        # Default attr is 'name'
        if attr is None:
            attr = 'name'

        # Default resource is path - in many APIs they are the same
        if resource is None:
            resource = path

        def getlist(kw):
            """Do list call, unwrap resource dict if present"""
            ret = self.list(path, **kw)
            if isinstance(ret, dict) and resource in ret:
                ret = ret[resource]
            return ret

        # Search by attribute
        kwargs = {attr: value}
        data = getlist(kwargs)
        if isinstance(data, dict):
            return data
        if len(data) == 1:
            return data[0]
        if len(data) > 1:
            msg = _("Multiple %(resource)s exist with %(attr)s='%(value)s'")
            raise exceptions.CommandError(msg % {
                'resource': resource,
                'attr': attr,
                'value': value
            })

        # Search by id
        kwargs = {'id': value}
        data = getlist(kwargs)
        if len(data) == 1:
            return data[0]
        msg = _("No %(resource)s with a %(attr)s or ID of '%(value)s' found")
        raise exceptions.CommandError(msg % {
            'resource': resource,
            'attr': attr,
            'value': value
        })
Beispiel #25
0
    def test_image_create_file(self, mock_open):
        mock_file = mock.MagicMock(name='File')
        mock_open.return_value = mock_file
        mock_open.read.return_value = (
            image_fakes.FakeImage.get_image_data(self.new_image))
        mock_exception = {
            'find.side_effect': exceptions.CommandError('x'),
        }
        self.images_mock.configure_mock(**mock_exception)

        arglist = [
            '--file', 'filer',
            ('--unprotected'
                if not self.new_image.protected else '--protected'),
            ('--public'
                if self.new_image.visibility == 'public' else '--private'),
            '--property', 'Alpha=1',
            '--property', 'Beta=2',
            '--tag', self.new_image.tags[0],
            '--tag', self.new_image.tags[1],
            self.new_image.name,
        ]
        verifylist = [
            ('file', 'filer'),
            ('protected', self.new_image.protected),
            ('unprotected', not self.new_image.protected),
            ('public', self.new_image.visibility == 'public'),
            ('private', self.new_image.visibility == 'private'),
            ('properties', {'Alpha': '1', 'Beta': '2'}),
            ('tags', self.new_image.tags),
            ('name', self.new_image.name),
        ]
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        # In base command class ShowOne in cliff, abstract method take_action()
        # returns a two-part tuple with a tuple of column names and a tuple of
        # data to be shown.
        columns, data = self.cmd.take_action(parsed_args)

        # ImageManager.create(name=, **)
        self.images_mock.create.assert_called_with(
            name=self.new_image.name,
            container_format=image.DEFAULT_CONTAINER_FORMAT,
            disk_format=image.DEFAULT_DISK_FORMAT,
            protected=self.new_image.protected,
            visibility=self.new_image.visibility,
            Alpha='1',
            Beta='2',
            tags=self.new_image.tags,
        )

        # Verify update() was not called, if it was show the args
        self.assertEqual(self.images_mock.update.call_args_list, [])

        self.images_mock.upload.assert_called_with(
            mock.ANY, mock.ANY,
        )

        self.assertEqual(
            image_fakes.FakeImage.get_image_columns(self.new_image),
            columns)
        self.assertEqual(
            image_fakes.FakeImage.get_image_data(self.new_image),
            data)
Beispiel #26
0
    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)', parsed_args)

        client = self.app.client_manager.orchestration

        tpl_files, template = template_utils.process_template_path(
            parsed_args.template,
            object_request=http.authenticated_fetcher(client))

        env_files_list = []
        env_files, env = (
            template_utils.process_multiple_environments_and_files(
                env_paths=parsed_args.environment,
                env_list_tracker=env_files_list))

        parameters = heat_utils.format_all_parameters(
            parsed_args.parameter,
            parsed_args.parameter_file,
            parsed_args.template)

        if parsed_args.pre_create:
            template_utils.hooks_to_env(env, parsed_args.pre_create,
                                        'pre-create')

        fields = {
            'stack_name': parsed_args.name,
            'disable_rollback': not parsed_args.enable_rollback,
            'parameters': parameters,
            'template': template,
            'files': dict(list(tpl_files.items()) + list(env_files.items())),
            'environment': env
        }

        # If one or more environments is found, pass the listing to the server
        if env_files_list:
            fields['environment_files'] = env_files_list

        if parsed_args.tags:
            fields['tags'] = parsed_args.tags
        if parsed_args.timeout:
            fields['timeout_mins'] = parsed_args.timeout

        if parsed_args.dry_run:
            stack = client.stacks.preview(**fields)

            formatters = {
                'description': heat_utils.text_wrap_formatter,
                'template_description': heat_utils.text_wrap_formatter,
                'stack_status_reason': heat_utils.text_wrap_formatter,
                'parameters': heat_utils.json_formatter,
                'outputs': heat_utils.json_formatter,
                'resources': heat_utils.json_formatter,
                'links': heat_utils.link_formatter,
            }

            columns = []
            for key in stack.to_dict():
                columns.append(key)
            columns.sort()

            return (
                columns,
                utils.get_item_properties(stack, columns,
                                          formatters=formatters)
            )

        stack = client.stacks.create(**fields)['stack']
        if parsed_args.wait:
            stack_status, msg = event_utils.poll_for_events(
                client, parsed_args.name, action='CREATE')
            if stack_status == 'CREATE_FAILED':
                raise exc.CommandError(msg)

        return _show_stack(client, stack['id'], format='table', short=True)
Beispiel #27
0
 def take_action_network(self, client, parsed_args):
     msg = _("Floating ip pool operations are only available for "
             "Compute v2 network.")
     raise exceptions.CommandError(msg)
Beispiel #28
0
    def take_action(self, parsed_args):
        self.log.debug('take_action(%s)', parsed_args)

        client = self.app.client_manager.orchestration

        tpl_files, template = template_utils.process_template_path(
            parsed_args.template,
            object_request=http.authenticated_fetcher(client),
            existing=parsed_args.existing)

        env_files_list = []
        env_files, env = (
            template_utils.process_multiple_environments_and_files(
                env_paths=parsed_args.environment,
                env_list_tracker=env_files_list))

        parameters = heat_utils.format_all_parameters(
            parsed_args.parameter,
            parsed_args.parameter_file,
            parsed_args.template)

        if parsed_args.pre_update:
            template_utils.hooks_to_env(env, parsed_args.pre_update,
                                        'pre-update')

        fields = {
            'stack_id': parsed_args.stack,
            'parameters': parameters,
            'existing': parsed_args.existing,
            'template': template,
            'files': dict(list(tpl_files.items()) + list(env_files.items())),
            'environment': env
        }

        # If one or more environments is found, pass the listing to the server
        if env_files_list:
            fields['environment_files'] = env_files_list

        if parsed_args.tags:
            fields['tags'] = parsed_args.tags
        if parsed_args.timeout:
            fields['timeout_mins'] = parsed_args.timeout
        if parsed_args.clear_parameter:
            fields['clear_parameters'] = list(parsed_args.clear_parameter)

        if parsed_args.rollback:
            rollback = parsed_args.rollback.strip().lower()
            if rollback not in ('enabled', 'disabled', 'keep'):
                msg = _('--rollback invalid value: %s') % parsed_args.rollback
                raise exc.CommandError(msg)
            if rollback != 'keep':
                fields['disable_rollback'] = rollback == 'disabled'

        if parsed_args.dry_run:
            changes = client.stacks.preview_update(**fields)

            fields = ['state', 'resource_name', 'resource_type',
                      'resource_identity']

            columns = sorted(changes.get("resource_changes", {}).keys())
            data = [heat_utils.json_formatter(changes["resource_changes"][key])
                    for key in columns]

            return columns, data

        if parsed_args.wait:
            # find the last event to use as the marker
            events = event_utils.get_events(client,
                                            stack_id=parsed_args.stack,
                                            event_args={'sort_dir': 'desc'},
                                            limit=1)
            marker = events[0].id if events else None

        client.stacks.update(**fields)

        if parsed_args.wait:
            stack = client.stacks.get(parsed_args.stack)
            stack_status, msg = event_utils.poll_for_events(
                client, stack.stack_name, action='UPDATE', marker=marker)
            if stack_status == 'UPDATE_FAILED':
                raise exc.CommandError(msg)

        return _show_stack(client, parsed_args.stack, format='table',
                           short=True)
Beispiel #29
0
    def take_action(self, parsed_args):

        http = self.app.client_manager.placement

        if parsed_args.aggregate:
            self.check_version(version.ge('1.3'))
            filters = {'member_of': parsed_args.uuid}
            url = common.url_with_filters(RP_BASE_URL, filters)
            rps = http.request('GET', url).json()['resource_providers']
            if not rps:
                raise exceptions.CommandError(
                    'No resource providers found in aggregate with uuid %s.' %
                    parsed_args.uuid)
        else:
            url = RP_BASE_URL + '/' + parsed_args.uuid
            rps = [http.request('GET', url).json()]

        resources_list = []
        ret = 0
        for rp in rps:
            inventories = collections.defaultdict(dict)
            url = BASE_URL.format(uuid=rp['uuid'])
            if parsed_args.amend:
                # Get existing inventories
                # TODO(melwitt): Do something to handle the possibility of the
                # GET failing here (example: resource provider deleted from
                # underneath us).
                payload = http.request('GET', url).json()
                inventories.update(payload['inventories'])
                payload['inventories'] = inventories
            else:
                payload = {
                    'inventories': inventories,
                    'resource_provider_generation': rp['generation']
                }

            # Apply resource values to inventories
            for r in parsed_args.resource:
                name, field, value = parse_resource_argument(r)
                inventories[name][field] = value

            try:
                if not parsed_args.dry_run:
                    resources = http.request('PUT', url, json=payload).json()
                else:
                    resources = payload
            except Exception as exp:
                with excutils.save_and_reraise_exception() as err_ctx:
                    if parsed_args.aggregate:
                        self.log.error(
                            _('Failed to set inventory for '
                              'resource provider %(rp)s: %(exp)s.'), {
                                  'rp': rp['uuid'],
                                  'exp': exp
                              })
                        err_ctx.reraise = False
                        ret += 1
                        continue
            resources_list.append((rp['uuid'], resources))

        if ret > 0:
            msg = _('Failed to set inventory for %(ret)s of %(total)s '
                    'resource providers.') % {
                        'ret': ret,
                        'total': len(rps)
                    }
            raise exceptions.CommandError(msg)

        def get_rows(fields, resources, rp_uuid=None):
            inventories = [
                dict(resource_class=k, **v)
                for k, v in resources['inventories'].items()
            ]
            prepend = (rp_uuid, ) if rp_uuid else ()
            # This is a generator expression
            rows = (prepend + utils.get_dict_properties(i, fields)
                    for i in inventories)
            return rows

        fields = ('resource_class', ) + FIELDS
        if parsed_args.aggregate:
            # If this is an aggregate batch, create output that will include
            # resource provider as the first field to differentiate the values
            rows = ()
            for rp_uuid, resources in resources_list:
                subrows = get_rows(fields, resources, rp_uuid=rp_uuid)
                rows = itertools.chain(rows, subrows)
            fields = ('resource_provider', ) + fields
            return fields, rows
        else:
            # If this was not an aggregate batch, show output for the one
            # resource provider (show payload of the first item in the list),
            # keeping the behavior prior to the addition of --aggregate option
            return fields, get_rows(fields, resources_list[0][1])
 def _validate_args(self, parsed_args):
     if parsed_args.stack in (None, ''):
         raise oscexc.CommandError("You must specify a stack name")