Exemple #1
0
 def parse_manifest(self):
     try:
         manifest = k8s_manifest.parse(self._get_manifest())
     except ValueError as e:
         raise exception.InvalidParameterValue(message=str(e))
     try:
         self.name = manifest["metadata"]["name"]
     except (KeyError, TypeError):
         raise exception.InvalidParameterValue(
             _("Field metadata['name'] can't be empty in manifest."))
     try:
         self.replicas = manifest["spec"]["replicas"]
     except (KeyError, TypeError):
         pass
     try:
         self.selector = manifest["spec"]["selector"]
     except (KeyError, TypeError):
         raise exception.InvalidParameterValue(
             _("Field spec['selector'] can't be empty in manifest."))
     try:
         self.labels = manifest["spec"]["template"]["metadata"]["labels"]
     except (KeyError, TypeError):
         raise exception.InvalidParameterValue(_(
             "Field spec['template']['metadata']['labels'] "
             "can't be empty in manifest."))
     try:
         images = []
         for cont in manifest["spec"]["template"]["spec"]["containers"]:
             images.append(cont["image"])
         self.images = images
     except (KeyError, TypeError):
         raise exception.InvalidParameterValue(_(
             "Field spec['template']['spec']['containers'] "
             "can't be empty in manifest."))
Exemple #2
0
 def get_validate_region_name(self, region_name):
     if region_name is None:
         message = _("region_name needs to be configured in magnum.conf")
         raise exception.InvalidParameterValue(message)
     """matches the region of a public endpoint for the Keystone
     service."""
     try:
         regions = self.client.regions.list()
     except kc_exception.NotFound:
         pass
     except Exception:
         LOG.exception(_LE('Failed to list regions'))
         raise exception.RegionsListFailed()
     region_list = []
     for region in regions:
         region_list.append(region.id)
     if region_name not in region_list:
         raise exception.InvalidParameterValue(
             _('region_name %(region_name)s is invalid, '
               'expecting a region_name in %(region_name_list)s.') %
             {
                 'region_name': region_name,
                 'region_name_list': '/'.join(region_list + ['unspecified'])
             })
     return region_name
Exemple #3
0
def validate_federation_properties(properties):
    """Validate Federation `properties` parameter."""
    if properties is None:
        raise exception.InvalidParameterValue("Please specify a `properties` "
                                              "dict for the federation.")
    # Currently, we only support the property `dns-zone`.
    if properties.get('dns-zone') is None:
        raise exception.InvalidParameterValue("No DNS zone specified. "
                                              "Please specify a `dns-zone`.")
Exemple #4
0
 def parse_manifest(self):
     try:
         manifest = k8s_manifest.parse(self._get_manifest())
     except ValueError as e:
         raise exception.InvalidParameterValue(message=str(e))
     try:
         self.name = manifest["id"]
     except KeyError:
         raise exception.InvalidParameterValue(
             "'id' can't be empty in manifest.")
     if "labels" in manifest:
         self.labels = manifest["labels"]
Exemple #5
0
    def bay_update(self, context, bay):
        LOG.debug('bay_heat bay_update')

        osc = clients.OpenStackClients(context)
        stack = osc.heat().stacks.get(bay.stack_id)
        if (stack.stack_status != bay_status.CREATE_COMPLETE
                and stack.stack_status != bay_status.UPDATE_COMPLETE):
            operation = _('Updating a bay when stack status is '
                          '"%s"') % stack.stack_status
            raise exception.NotSupported(operation=operation)

        delta = set(bay.obj_what_changed())
        if 'node_count' in delta:
            delta.remove('node_count')
            manager = scale_manager.ScaleManager(context, osc, bay)

            _update_stack(context, osc, bay, manager)
            self._poll_and_check(osc, bay)

        if delta:
            raise exception.InvalidParameterValue(
                err=("cannot change bay property(ies) %s." % ", ".join(delta)))

        bay.save()
        return bay
Exemple #6
0
def validate_cluster_properties(delta):

    update_disallowed_properties = delta - cluster_update_allowed_properties
    if update_disallowed_properties:
        err = (_("cannot change cluster property(ies) %s.") %
               ", ".join(update_disallowed_properties))
        raise exception.InvalidParameterValue(err=err)
Exemple #7
0
    def bay_create(self, context, bay, bay_create_timeout):
        LOG.debug('bay_heat bay_create')

        osc = clients.OpenStackClients(context)

        baymodel = objects.BayModel.get_by_uuid(context,
                                                bay.baymodel_id)
        if baymodel.registry_enabled:
            trust = osc.keystone().create_trust(
                CONF.docker_registry.trustee_user_id,
                CONF.docker_registry.trust_roles)
            bay.registry_trust_id = trust.id

        try:
            # Generate certificate and set the cert reference to bay
            bay.uuid = uuid.uuid4()
            cert_manager.generate_certificates_to_bay(bay)
            created_stack = _create_stack(context, osc, bay,
                                          bay_create_timeout)
        except exc.HTTPBadRequest as e:
            cert_manager.delete_certificates_from_bay(bay)
            raise exception.InvalidParameterValue(message=six.text_type(e))
        except Exception:
            raise

        bay.stack_id = created_stack['stack']['id']
        bay.create()

        self._poll_and_check(osc, bay)

        return bay
Exemple #8
0
    def wrapper(func, *args, **kwargs):
        # Note(eliqiao): This decorator has some assumptions
        # args[1] should be an APIBase instance or
        # args[2] should be a cluster_ident
        obj = args[1]
        if hasattr(obj, 'cluster_uuid'):
            cluster = objects.Cluster.get_by_uuid(pecan.request.context,
                                                  obj.cluster_uuid)
        else:
            cluster_ident = args[2]
            if uuidutils.is_uuid_like(cluster_ident):
                cluster = objects.Cluster.get_by_uuid(pecan.request.context,
                                                      cluster_ident)
            else:
                cluster = objects.Cluster.get_by_name(pecan.request.context,
                                                      cluster_ident)

        if cluster.cluster_template.coe not in cluster_types:
            raise exception.InvalidParameterValue(
                _('Cannot fulfill request with a %(cluster_type)s cluster, '
                  'expecting a %(supported_cluster_types)s cluster.') % {
                      'cluster_type': cluster.cluster_template.coe,
                      'supported_cluster_types': '/'.join(cluster_types)
                  })

        return func(*args, **kwargs)
Exemple #9
0
    def update_x509keypair(self, x509keypair_id, values):
        # NOTE(dtantsur): this can lead to very strange errors
        if 'uuid' in values:
            msg = _("Cannot overwrite UUID for an existing X509KeyPair.")
            raise exception.InvalidParameterValue(err=msg)

        return self._do_update_x509keypair(x509keypair_id, values)
Exemple #10
0
    def update_pod(self, pod_id, values):
        # NOTE(dtantsur): this can lead to very strange errors
        if 'uuid' in values:
            msg = _("Cannot overwrite UUID for an existing Pod.")
            raise exception.InvalidParameterValue(err=msg)

        return self._do_update_pod(pod_id, values)
Exemple #11
0
def validate_labels_image_providers(labels):
    """Validate mesos_agent_image_providers"""
    mesos_agent_image_providers = labels.get('mesos_agent_image_providers')
    mesos_agent_image_providers_list = mesos_agent_image_providers.split(',')
    isolation_with_valid_data = False
    for image_providers_val in mesos_agent_image_providers_list:
        image_providers_val = image_providers_val.lower()
        if image_providers_val not in SUPPORTED_IMAGE_PROVIDERS:
            raise exception.InvalidParameterValue(
                _('property "labels/mesos_agent_image_providers" with value '
                  '"%(image_providers)s" is not supported, supported values '
                  'are: %(supported_image_providers)s') % {
                      'image_providers':
                      image_providers_val,
                      'supported_image_providers':
                      ', '.join(SUPPORTED_IMAGE_PROVIDERS + ['unspecified'])
                  })

        if image_providers_val == 'docker':
            mesos_agent_isolation = labels.get('mesos_agent_isolation')
            if mesos_agent_isolation is not None:
                mesos_agent_isolation_list = mesos_agent_isolation.split(',')
                for isolations_val in mesos_agent_isolation_list:
                    if isolations_val == 'docker/runtime':
                        isolation_with_valid_data = True
            if mesos_agent_isolation is None or not isolation_with_valid_data:
                raise exception.RequiredParameterNotProvided(
                    _("Docker runtime isolator has to be specified if 'docker' "
                      "is included in 'mesos_agent_image_providers' Please add "
                      "'docker/runtime' to 'mesos_agent_isolation' labels "
                      "flags"))
Exemple #12
0
    def update_cluster_template(self, cluster_template_id, values):
        # NOTE(dtantsur): this can lead to very strange errors
        if 'uuid' in values:
            msg = _("Cannot overwrite UUID for an existing ClusterTemplate.")
            raise exception.InvalidParameterValue(err=msg)

        return self._do_update_cluster_template(cluster_template_id, values)
    def cluster_create(self, context, cluster, create_timeout):
        LOG.debug('cluster_heat cluster_create')

        osc = clients.OpenStackClients(context)

        try:
            # Create trustee/trust and set them to cluster
            trust_manager.create_trustee_and_trust(osc, cluster)
            # Generate certificate and set the cert reference to cluster
            cert_manager.generate_certificates_to_cluster(cluster,
                                                          context=context)
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_PENDING)
            created_stack = _create_stack(context, osc, cluster,
                                          create_timeout)
        except Exception as e:
            cluster.status = fields.ClusterStatus.CREATE_FAILED
            cluster.status_reason = six.text_type(e)
            cluster.create()
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_FAILURE)

            if isinstance(e, exc.HTTPBadRequest):
                e = exception.InvalidParameterValue(message=six.text_type(e))

                raise e
            raise

        cluster.stack_id = created_stack['stack']['id']
        cluster.status = fields.ClusterStatus.CREATE_IN_PROGRESS
        cluster.create()

        self._poll_and_check(osc, cluster)

        return cluster
Exemple #14
0
    def bay_create(self, context, bay, bay_create_timeout):
        LOG.debug('bay_heat bay_create')

        osc = clients.OpenStackClients(context)

        bay.uuid = uuid.uuid4()
        try:
            # Create trustee/trust and set them to bay
            trust_manager.create_trustee_and_trust(osc, bay)
            # Generate certificate and set the cert reference to bay
            cert_manager.generate_certificates_to_bay(bay, context=context)
            conductor_utils.notify_about_bay_operation(
                context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_PENDING)
            created_stack = _create_stack(context, osc, bay,
                                          bay_create_timeout)
        except Exception as e:
            cert_manager.delete_certificates_from_bay(bay, context=context)
            trust_manager.delete_trustee_and_trust(osc, context, bay)
            conductor_utils.notify_about_bay_operation(
                context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_FAILURE)

            if isinstance(e, exc.HTTPBadRequest):
                e = exception.InvalidParameterValue(message=six.text_type(e))

            raise e

        bay.stack_id = created_stack['stack']['id']
        bay.status = bay_status.CREATE_IN_PROGRESS
        bay.create()

        self._poll_and_check(osc, bay)

        return bay
Exemple #15
0
    def cluster_update(self, context, cluster, node_count, rollback=False):
        LOG.debug('cluster_heat cluster_update')

        osc = clients.OpenStackClients(context)
        allow_update_status = (fields.ClusterStatus.CREATE_COMPLETE,
                               fields.ClusterStatus.UPDATE_COMPLETE,
                               fields.ClusterStatus.RESUME_COMPLETE,
                               fields.ClusterStatus.RESTORE_COMPLETE,
                               fields.ClusterStatus.ROLLBACK_COMPLETE,
                               fields.ClusterStatus.SNAPSHOT_COMPLETE,
                               fields.ClusterStatus.CHECK_COMPLETE,
                               fields.ClusterStatus.ADOPT_COMPLETE)
        if cluster.status not in allow_update_status:
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_UPDATE, taxonomy.OUTCOME_FAILURE)
            operation = _('Updating a cluster when status is '
                          '"%s"') % cluster.status
            raise exception.NotSupported(operation=operation)

        # Updates will be only reflected to the default worker
        # nodegroup.
        worker_ng = cluster.default_ng_worker
        if worker_ng.node_count == node_count:
            return
        # Backup the old node count so that we can restore it
        # in case of an exception.
        old_node_count = worker_ng.node_count

        manager = scale_manager.get_scale_manager(context, osc, cluster)

        # Get driver
        ct = conductor_utils.retrieve_cluster_template(context, cluster)
        cluster_driver = driver.Driver.get_driver(ct.server_type,
                                                  ct.cluster_distro, ct.coe)
        # Update cluster
        try:
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_UPDATE, taxonomy.OUTCOME_PENDING)
            worker_ng.node_count = node_count
            worker_ng.save()
            cluster_driver.update_cluster(context, cluster, manager, rollback)
            cluster.status = fields.ClusterStatus.UPDATE_IN_PROGRESS
            cluster.status_reason = None
        except Exception as e:
            cluster.status = fields.ClusterStatus.UPDATE_FAILED
            cluster.status_reason = six.text_type(e)
            cluster.save()
            # Restore the node_count
            worker_ng.node_count = old_node_count
            worker_ng.save()
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_UPDATE, taxonomy.OUTCOME_FAILURE)
            if isinstance(e, exc.HTTPBadRequest):
                e = exception.InvalidParameterValue(message=six.text_type(e))
                raise e
            raise

        cluster.save()
        return cluster
Exemple #16
0
    def cluster_upgrade(self,
                        context,
                        cluster,
                        cluster_template,
                        max_batch_size,
                        nodegroup,
                        rollback=False):
        LOG.debug('cluster_conductor cluster_upgrade')

        # osc = clients.OpenStackClients(context)
        allow_update_status = (fields.ClusterStatus.CREATE_COMPLETE,
                               fields.ClusterStatus.UPDATE_COMPLETE,
                               fields.ClusterStatus.RESUME_COMPLETE,
                               fields.ClusterStatus.RESTORE_COMPLETE,
                               fields.ClusterStatus.ROLLBACK_COMPLETE,
                               fields.ClusterStatus.SNAPSHOT_COMPLETE,
                               fields.ClusterStatus.CHECK_COMPLETE,
                               fields.ClusterStatus.ADOPT_COMPLETE)
        if cluster.status not in allow_update_status:
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_UPDATE, taxonomy.OUTCOME_FAILURE,
                cluster)
            operation = _('Upgrading a cluster when status is '
                          '"%s"') % cluster.status
            raise exception.NotSupported(operation=operation)

        # Get driver
        ct = conductor_utils.retrieve_cluster_template(context, cluster)
        cluster_driver = driver.Driver.get_driver(ct.server_type,
                                                  ct.cluster_distro, ct.coe)
        # Upgrade cluster
        try:
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_UPDATE, taxonomy.OUTCOME_PENDING,
                cluster)
            cluster_driver.upgrade_cluster(context, cluster, cluster_template,
                                           max_batch_size, nodegroup, rollback)
            cluster.status = fields.ClusterStatus.UPDATE_IN_PROGRESS
            nodegroup.status = fields.ClusterStatus.UPDATE_IN_PROGRESS
            cluster.status_reason = None
        except Exception as e:
            cluster.status = fields.ClusterStatus.UPDATE_FAILED
            cluster.status_reason = six.text_type(e)
            cluster.save()
            nodegroup.status = fields.ClusterStatus.UPDATE_FAILED
            nodegroup.status_reason = six.text_type(e)
            nodegroup.save()
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_UPDATE, taxonomy.OUTCOME_FAILURE,
                cluster)
            if isinstance(e, exc.HTTPBadRequest):
                e = exception.InvalidParameterValue(message=six.text_type(e))
                raise e
            raise

        nodegroup.save()
        cluster.save()
        return cluster
Exemple #17
0
 def _validate_network_driver_supported(cls, driver):
     """Confirm that driver is supported by Magnum for this COE."""
     if driver not in cls.supported_network_drivers:
         raise exception.InvalidParameterValue(_(
             'Network driver type %(driver)s is not supported, '
             'expecting a %(supported_drivers)s network driver.') % {
                 'driver': driver,
                 'supported_drivers': '/'.join(
                     cls.supported_network_drivers + ['unspecified'])})
Exemple #18
0
def validate_labels_executor_env_variables(labels):
    """Validate executor_environment_variables"""
    mesos_agent_executor_env_val = labels.get(
        'mesos_agent_executor_env_variables')
    try:
        json.loads(mesos_agent_executor_env_val)
    except ValueError:
        err = (_("Json format error"))
        raise exception.InvalidParameterValue(err)
Exemple #19
0
 def _validate_volume_driver_supported(cls, driver):
     """Confirm that volume driver is supported by Magnum for this COE."""
     if driver not in cls.supported_volume_driver:
         raise exception.InvalidParameterValue(_(
             'Volume driver type %(driver)s is not supported, '
             'expecting a %(supported_volume_driver)s volume driver.') % {
                 'driver': driver,
                 'supported_volume_driver': '/'.join(
                     cls.supported_volume_driver + ['unspecified'])})
Exemple #20
0
 def _validate_server_type(cls, server_type):
     """Confirm that server type is supported by Magnum for this COE."""
     if server_type not in cls.supported_server_types:
         raise exception.InvalidParameterValue(_(
             'Server type %(server_type)s is not supported, '
             'expecting a %(supported_server_types)s server type.') % {
                 'server_type': server_type,
                 'supported_server_types': '/'.join(
                     cls.supported_server_types + ['unspecified'])})
Exemple #21
0
    def cluster_create(self, context, cluster, master_count, node_count,
                       create_timeout):
        LOG.debug('cluster_heat cluster_create')

        osc = clients.OpenStackClients(context)

        cluster.status = fields.ClusterStatus.CREATE_IN_PROGRESS
        cluster.status_reason = None
        cluster.create()

        # Master nodegroup
        master_ng = conductor_utils._get_nodegroup_object(context,
                                                          cluster,
                                                          master_count,
                                                          is_master=True)
        master_ng.create()
        # Minion nodegroup
        minion_ng = conductor_utils._get_nodegroup_object(context,
                                                          cluster,
                                                          node_count,
                                                          is_master=False)
        minion_ng.create()

        try:
            # Create trustee/trust and set them to cluster
            trust_manager.create_trustee_and_trust(osc, cluster)
            # Generate certificate and set the cert reference to cluster
            cert_manager.generate_certificates_to_cluster(cluster,
                                                          context=context)
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_PENDING,
                cluster)
            # Get driver
            cluster_driver = driver.Driver.get_driver_for_cluster(
                context, cluster)
            # Create cluster
            cluster_driver.create_cluster(context, cluster, create_timeout)
            cluster.save()
            for ng in cluster.nodegroups:
                ng.stack_id = cluster.stack_id
                ng.save()

        except Exception as e:
            cluster.status = fields.ClusterStatus.CREATE_FAILED
            cluster.status_reason = six.text_type(e)
            cluster.save()
            conductor_utils.notify_about_cluster_operation(
                context, taxonomy.ACTION_CREATE, taxonomy.OUTCOME_FAILURE,
                cluster)

            if isinstance(e, exc.HTTPBadRequest):
                e = exception.InvalidParameterValue(message=six.text_type(e))

                raise e
            raise

        return cluster
Exemple #22
0
 def get_coe_validator(cls, coe):
     if coe == 'kubernetes':
         return K8sValidator()
     elif coe == 'swarm':
         return SwarmValidator()
     elif coe == 'mesos':
         return MesosValidator()
     else:
         raise exception.InvalidParameterValue(
             _('Requested COE type %s is not supported.') % coe)
Exemple #23
0
 def _validate_network_driver_allowed(cls, driver):
     """Confirm that driver is allowed via configuration for this COE."""
     if ('all' not in cls.allowed_network_drivers and
        driver not in cls.allowed_network_drivers):
         raise exception.InvalidParameterValue(_(
             'Network driver type %(driver)s is not allowed, '
             'expecting a %(allowed_drivers)s network driver. ') % {
                 'driver': driver,
                 'allowed_drivers': '/'.join(
                     cls.allowed_network_drivers + ['unspecified'])})
Exemple #24
0
 def parse_manifest(self):
     try:
         manifest = k8s_manifest.parse(self._get_manifest())
     except ValueError as e:
         raise exception.InvalidParameterValue(message=str(e))
     try:
         self.name = manifest["metadata"]["name"]
     except (KeyError, TypeError):
         raise exception.InvalidParameterValue(
             "Field metadata['name'] can't be empty in manifest.")
     images = []
     try:
         for container in manifest["spec"]["containers"]:
             images.append(container["image"])
         self.images = images
     except (KeyError, TypeError):
         raise exception.InvalidParameterValue(
             "Field spec['containers'] can't be empty in manifest.")
     if "labels" in manifest["metadata"]:
         self.labels = manifest["metadata"]["labels"]
Exemple #25
0
def _enforce_volume_storage_size(cluster_template):
    if not cluster_template.get('docker_volume_size'):
        return
    volume_size = cluster_template.get('docker_volume_size')
    storage_driver = cluster_template.get('docker_storage_driver')
    if storage_driver == 'devicemapper':
        if volume_size < 3:
            raise exception.InvalidParameterValue(
                'docker volume size %s GB is not valid, '
                'expecting minimum value 3GB for %s storage '
                'driver.') % (volume_size, storage_driver)
Exemple #26
0
    def parse_manifest(self):
        try:
            manifest = k8s_manifest.parse(self._get_manifest())
        except ValueError as e:
            raise exception.InvalidParameterValue(message=str(e))
        try:
            self.name = manifest["metadata"]["name"]
        except (KeyError, TypeError):
            raise exception.InvalidParameterValue(
                "Field metadata['name'] can't be empty in manifest.")
        try:
            self.ports = manifest["spec"]["ports"][:]
        except (KeyError, TypeError):
            raise exception.InvalidParameterValue(
                "Field spec['ports'] can't be empty in manifest.")

        if "selector" in manifest["spec"]:
            self.selector = manifest["spec"]["selector"]
        if "labels" in manifest["metadata"]:
            self.labels = manifest["metadata"]["labels"]
Exemple #27
0
    def update_node(self, node_id, values):
        # NOTE(dtantsur): this can lead to very strange errors
        if 'uuid' in values:
            msg = _("Cannot overwrite UUID for an existing Node.")
            raise exception.InvalidParameterValue(err=msg)

        try:
            return self._do_update_node(node_id, values)
        except db_exc.DBDuplicateEntry:
            raise exception.InstanceAssociated(
                instance_uuid=values['ironic_node_id'], node=node_id)
Exemple #28
0
 def get_coe_validator(cls, coe):
     if not cls.validators:
         cls.validators = {
             'kubernetes': K8sValidator(),
             'swarm': SwarmValidator(),
             'mesos': MesosValidator(),
         }
     if coe in cls.validators:
         return cls.validators[coe]
     else:
         raise exception.InvalidParameterValue(
             _('Requested COE type %s is not supported.') % coe)
Exemple #29
0
def validate_federation_hostcluster(cluster_uuid):
    """Validate Federation `hostcluster_id` parameter.

    If the parameter was not specified raise an
    `exceptions.InvalidParameterValue`. If the specified identifier does not
    identify any Cluster, raise `exception.ClusterNotFound`
    """
    if cluster_uuid is not None:
        api_utils.get_resource('Cluster', cluster_uuid)
    else:
        raise exception.InvalidParameterValue(
            "No hostcluster specified. "
            "Please specify a hostcluster_id.")
Exemple #30
0
def validate_labels_strategy(labels):
    """Validate swarm_strategy"""
    swarm_strategy = list(labels.get('swarm_strategy', "").split())
    unsupported_strategy = set(swarm_strategy) - set(
        SUPPORTED_SWARM_STRATEGY)
    if (len(unsupported_strategy) > 0):
        raise exception.InvalidParameterValue(_(
            'property "labels/swarm_strategy" with value '
            '"%(strategy)s" is not supported, supported values are: '
            '%(supported_strategies)s') % {
                'strategy': ' '.join(list(unsupported_strategy)),
                'supported_strategies': ', '.join(
                    SUPPORTED_SWARM_STRATEGY + ['unspecified'])})