# Copyright 2016 EasyStack. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from magnumclient.common import utils as magnum_utils from magnumclient import exceptions from magnumclient.i18n import _ from osc_lib.command import command from osc_lib import utils CLUSTER_ATTRIBUTES = [ 'status', 'cluster_template_id', 'node_addresses', 'uuid', 'stack_id', 'status_reason', 'created_at', 'updated_at', 'coe_version', 'labels', 'faults', 'keypair', 'api_address', 'master_addresses', 'create_timeout', 'node_count', 'discovery_url', 'master_count', 'container_version', 'name', 'master_flavor_id', 'flavor_id', ] class CreateCluster(command.Command): _description = _("Create a cluster") def get_parser(self, prog_name): parser = super(CreateCluster, self).get_parser(prog_name) # NOTE: All arguments are positional and, if not provided # with a default, required. parser.add_argument('--cluster-template', dest='cluster_template', required=True, metavar='', help='ID or name of the cluster template.') parser.add_argument('--discovery-url', dest='discovery_url', metavar='', help=('Specifies custom delivery url for ' 'node discovery.')) parser.add_argument('--docker-volume-size', dest='docker_volume_size', type=int, metavar='', help=('The size in GB for the docker volume to ' 'use.')) parser.add_argument('--labels', metavar='', action='append', default=[], help=_('Arbitrary labels in the form of key=value' 'pairs to associate with a cluster ' 'template. May be used multiple times.')) parser.add_argument('--keypair', default=None, metavar='', help='UUID or name of the keypair to use.') parser.add_argument('--master-count', dest='master_count', type=int, default=1, metavar='', help='The number of master nodes for the cluster.') parser.add_argument('name', metavar='', help='Name of the cluster to create.') parser.add_argument('--node-count', dest='node_count', type=int, default=1, metavar='', help='The cluster node count.') parser.add_argument('--timeout', type=int, default=60, metavar='', help=('The timeout for cluster creation time. The ' 'default is 60 minutes.')) parser.add_argument( '--master-flavor', dest='master_flavor', metavar='', help=_('The nova flavor name or UUID to use when launching the ' 'master node of the Cluster.')) parser.add_argument( '--flavor', metavar='', help=_('The nova flavor name or UUID to use when launching the ' 'Cluster.')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra args = { 'cluster_template_id': parsed_args.cluster_template, 'create_timeout': parsed_args.timeout, 'discovery_url': parsed_args.discovery_url, 'docker_volume_size': parsed_args.docker_volume_size, 'keypair': parsed_args.keypair, 'master_count': parsed_args.master_count, 'name': parsed_args.name, 'node_count': parsed_args.node_count, 'master_flavor_id': parsed_args.master_flavor, 'flavor_id': parsed_args.flavor, } if parsed_args.labels is not None: args['labels'] = magnum_utils.handle_labels(parsed_args.labels) cluster = mag_client.clusters.create(**args) print("Request to create cluster %s accepted" % cluster.uuid) class DeleteCluster(command.Command): _description = _("Delete a cluster") def get_parser(self, prog_name): parser = super(DeleteCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', nargs='+', metavar='', help='ID or name of the cluster(s) to delete.') return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra for cluster in parsed_args.cluster: mag_client.clusters.delete(cluster) print("Request to delete cluster %s has been accepted." % cluster) class ListCluster(command.Lister): _description = _("List clusters") def get_parser(self, prog_name): parser = super(ListCluster, self).get_parser(prog_name) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of clusters to return')) parser.add_argument( '--sort-key', metavar='', help=_('Column to sort results by')) parser.add_argument( '--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra columns = [ 'uuid', 'name', 'keypair', 'node_count', 'master_count', 'status'] clusters = mag_client.clusters.list(limit=parsed_args.limit, sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir) return ( columns, (utils.get_item_properties(c, columns) for c in clusters) ) class ShowCluster(command.ShowOne): _description = _("Show a Cluster") def get_parser(self, prog_name): parser = super(ShowCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', metavar='', help=_('ID or name of the cluster to show.') ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) columns = CLUSTER_ATTRIBUTES mag_client = self.app.client_manager.container_infra cluster = mag_client.clusters.get(parsed_args.cluster) return (columns, utils.get_item_properties(cluster, columns)) class UpdateCluster(command.Command): _description = _("Update a Cluster") def get_parser(self, prog_name): parser = super(UpdateCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', metavar='', help=_('The name or UUID of cluster to update')) parser.add_argument( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operations: one of 'add', 'replace' or 'remove'")) parser.add_argument( 'attributes', metavar='', nargs='+', action='append', default=[], help=_( "Attributes to add/replace or remove (only PATH is necessary " "on remove)")) parser.add_argument( '--rollback', action='store_true', dest='rollback', default=False, help=_('Rollback cluster on update failure.')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra patch = magnum_utils.args_array_to_patch(parsed_args.op, parsed_args.attributes[0]) mag_client.clusters.update(parsed_args.cluster, patch) print("Request to update cluster %s has been accepted." % parsed_args.cluster) class ConfigCluster(command.Command): _description = _("Get Configuration for a Cluster") def get_parser(self, prog_name): parser = super(ConfigCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', metavar='', help=_('The name or UUID of cluster to update')) parser.add_argument( '--dir', metavar='', default='.', help=_('Directory to save the certificate and config files.')) parser.add_argument( '--force', action='store_true', dest='force', default=False, help=_('Overwrite files if existing.')) return parser def take_action(self, parsed_args): """Configure native client to access cluster. You can source the output of this command to get the native client of the corresponding COE configured to access the cluster. """ self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra parsed_args.dir = os.path.abspath(parsed_args.dir) cluster = mag_client.clusters.get(parsed_args.cluster) if cluster.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE', 'ROLLBACK_COMPLETE'): raise exceptions.CommandError("cluster in status %s" % cluster.status) cluster_template = mag_client.cluster_templates.get( cluster.cluster_template_id) opts = { 'cluster_uuid': cluster.uuid, } if not cluster_template.tls_disabled: tls = magnum_utils.generate_csr_and_key() tls['ca'] = mag_client.certificates.get(**opts).pem opts['csr'] = tls['csr'] tls['cert'] = mag_client.certificates.create(**opts).pem for k in ('key', 'cert', 'ca'): fname = "%s/%s.pem" % (parsed_args.dir, k) if os.path.exists(fname) and not parsed_args.force: raise Exception("File %s exists, aborting." % fname) else: f = open(fname, "w") f.write(tls[k]) f.close() print(magnum_utils.config_cluster(cluster, cluster_template, parsed_args.dir, force=parsed_args.force))