def _check_version(self, api_version): if api_version == 'latest': return LATEST_API_VERSION else: try: versions = tuple(int(i) for i in api_version.split('.')) except ValueError: versions = () if len(versions) == 1: # Default value of magnum_api_version is '1'. # If user not specify the value of api version, not passing # headers at all. magnum_api_version = None elif len(versions) == 2: magnum_api_version = api_version # In the case of '1.0' if versions[1] == 0: magnum_api_version = None else: msg = _("The requested API version %(ver)s is an unexpected " "format. Acceptable formats are 'X', 'X.Y', or the " "literal string '%(latest)s'.") % { 'ver': api_version, 'latest': 'latest' } raise exc.CommandError(msg) api_major_version = versions[0] return (api_major_version, magnum_api_version)
def _config_cluster_kubernetes(cluster, cluster_template, cfg_dir, force=False): """Return and write configuration for the given kubernetes cluster.""" cfg_file = "%s/config" % cfg_dir if cluster_template.tls_disabled: cfg = ("apiVersion: v1\n" "clusters:\n" "- cluster:\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " cluster: %(name)s\n" " user: %(name)s\n" " name: %(name)s\n" "current-context: %(name)s\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: %(name)s'\n" % { 'name': cluster.name, 'api_address': cluster.api_address }) else: cfg = ("apiVersion: v1\n" "clusters:\n" "- cluster:\n" " certificate-authority: %(cfg_dir)s/ca.pem\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " cluster: %(name)s\n" " user: %(name)s\n" " name: %(name)s\n" "current-context: %(name)s\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: %(name)s\n" " user:\n" " client-certificate: %(cfg_dir)s/cert.pem\n" " client-key: %(cfg_dir)s/key.pem\n" % { 'name': cluster.name, 'api_address': cluster.api_address, 'cfg_dir': cfg_dir }) if os.path.exists(cfg_file) and not force: raise exc.CommandError("File %s exists, aborting." % cfg_file) else: f = open(cfg_file, "w") f.write(cfg) f.close() if 'csh' in os.environ['SHELL']: return "setenv KUBECONFIG %s\n" % cfg_file else: return "export KUBECONFIG=%s\n" % cfg_file
def do_cluster_config(cs, 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. Example: eval $(magnum cluster-config <cluster-name>). """ cluster = cs.clusters.get(args.cluster) if cluster.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE'): raise exceptions.CommandError("cluster in status %s" % cluster.status) cluster_template = cs.cluster_templates.get(cluster.cluster_template_id) opts = { 'cluster_uuid': cluster.uuid, } if not cluster_template.tls_disabled: tls = _generate_csr_and_key() tls['ca'] = cs.certificates.get(**opts).pem opts['csr'] = tls['csr'] tls['cert'] = cs.certificates.create(**opts).pem for k in ('key', 'cert', 'ca'): fname = "%s/%s.pem" % (args.dir, k) if os.path.exists(fname) and not args.force: raise Exception("File %s exists, aborting." % fname) else: f = open(fname, "w") f.write(tls[k]) f.close() print( _config_cluster(cluster, cluster_template, cfg_dir=args.dir, force=args.force))
def format_labels(lbls, parse_comma=True): '''Reformat labels into dict of format expected by the API.''' if not lbls: return {} if parse_comma: # expect multiple invocations of --labels but fall back # to either , or ; delimited if only one --labels is specified if len(lbls) == 1 and lbls[0].count('=') > 1: lbls = lbls[0].replace(';', ',').split(',') labels = {} for l in lbls: try: (k, v) = l.split(('='), 1) except ValueError: raise exc.CommandError( _('labels must be a list of KEY=VALUE ' 'not %s') % l) if k not in labels: labels[k] = v else: labels[k] += ",%s" % v return labels
def do_bay_config(cs, args): """Configure native client to access bay. You can source the output of this command to get the native client of the corresponding COE configured to access the bay. Example: eval $(magnum bay-config <bay-name>). (Deprecated in favor of cluster-config.) """ args.dir = os.path.abspath(args.dir) bay = cs.bays.get(args.bay) if bay.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE'): raise exceptions.CommandError("Bay in status %s" % bay.status) baymodel = cs.baymodels.get(bay.baymodel_id) opts = { 'cluster_uuid': bay.uuid, } if not baymodel.tls_disabled: tls = _generate_csr_and_key() tls['ca'] = cs.certificates.get(**opts).pem opts['csr'] = tls['csr'] tls['cert'] = cs.certificates.create(**opts).pem for k in ('key', 'cert', 'ca'): fname = "%s/%s.pem" % (args.dir, k) if os.path.exists(fname) and not args.force: raise Exception("File %s exists, aborting." % fname) else: f = open(fname, "w") f.write(tls[k]) f.close() print(_config_bay(bay, baymodel, cfg_dir=args.dir, force=args.force))
def _config_bay_kubernetes(bay, baymodel, cfg_dir, force=False): """Return and write configuration for the given kubernetes bay.""" cfg_file = "%s/config" % cfg_dir if baymodel.tls_disabled: cfg = ("apiVersion: v1\n" "bays:\n" "- bay:\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " bay: %(name)s\n" " user: %(name)s\n" " name: default/%(name)s\n" "current-context: default/%(name)s\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: %(name)s'\n" % { 'name': bay.name, 'api_address': bay.api_address }) else: cfg = ("apiVersion: v1\n" "bays:\n" "- bay:\n" " certificate-authority: ca.pem\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " bay: %(name)s\n" " user: %(name)s\n" " name: default/%(name)s\n" "current-context: default/%(name)s\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: %(name)s\n" " user:\n" " client-certificate: cert.pem\n" " client-key: key.pem\n" % { 'name': bay.name, 'api_address': bay.api_address }) if os.path.exists(cfg_file) and not force: raise exceptions.CommandError("File %s exists, aborting." % cfg_file) else: f = open(cfg_file, "w") f.write(cfg) f.close() if 'csh' in os.environ['SHELL']: return "setenv KUBECONFIG %s\n" % cfg_file else: return "export KUBECONFIG=%s\n" % cfg_file
def do_help(self, args): """Display help about this program or one of its subcommands.""" # NOTE(jamespage): args.command is not guaranteed with python >= 3.4 command = getattr(args, 'command', '') if command: if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError("'%s' is not a valid subcommand" % args.command) else: self.parser.print_help()
def do_cluster_update(cs, args): """Update information about the given cluster.""" if args.rollback and args.magnum_api_version and \ args.magnum_api_version in ('1.0', '1.1', '1.2'): raise exceptions.CommandError("Rollback is not supported in API v%s. " "Please use API v1.3+." % args.magnum_api_version) patch = magnum_utils.args_array_to_patch(args.op, args.attributes[0]) cluster = cs.clusters.update(args.cluster, patch, args.rollback) if args.magnum_api_version and args.magnum_api_version == '1.1': _show_cluster(cluster) else: print("Request to update cluster %s has been accepted." % args.cluster)
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. """ if parsed_args.use_keystone: parsed_args.use_certificate = False if not parsed_args.use_certificate: parsed_args.use_keystone = True 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, } tls = None 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 if parsed_args.output_certs: 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: with open(fname, "w") as f: f.write(tls[k]) print( magnum_utils.config_cluster(cluster, cluster_template, parsed_args.dir, force=parsed_args.force, certs=tls, use_keystone=parsed_args.use_keystone))
def _ensure_auth_info(self, args): if not cliutils.isunauthenticated(args.func): if (not (args.os_token and (args.os_auth_url or args.os_endpoint_override)) and not args.os_cloud): if not (args.os_username or args.os_user_id): raise exc.CommandError( "You must provide a username via either --os-username " "or via env[OS_USERNAME]") if not args.os_password: raise exc.CommandError( "You must provide a password via either " "--os-password, env[OS_PASSWORD], or prompted " "response") if (not args.os_project_name and not args.os_project_id): raise exc.CommandError( "You must provide a project name or project id via " "--os-project-name, --os-project-id, " "env[OS_PROJECT_NAME] or env[OS_PROJECT_ID]") if not args.os_auth_url: raise exc.CommandError( "You must provide an auth url via either " "--os-auth-url or via env[OS_AUTH_URL]")
def args_array_to_patch(op, attributes): patch = [] for attr in attributes: # Sanitize if not attr.startswith('/'): attr = '/' + attr if op in ['add', 'replace']: path, value = split_and_deserialize(attr) patch.append({'op': op, 'path': path, 'value': value}) elif op == "remove": # For remove only the key is needed patch.append({'op': op, 'path': attr}) else: raise exc.CommandError(_('Unknown PATCH operation: %s') % op) return patch
def do_bay_update(cs, args): """Update information about the given bay. (Deprecated in favor of cluster-update.) """ if args.rollback and args.magnum_api_version and \ args.magnum_api_version in ('1.0', '1.1', '1.2'): raise exceptions.CommandError("Rollback is not supported in API v%s. " "Please use API v1.3+." % args.magnum_api_version) patch = magnum_utils.args_array_to_patch(args.op, args.attributes[0]) bay = cs.bays.update(args.bay, patch, args.rollback) if args.magnum_api_version and args.magnum_api_version == '1.1': _show_bay(bay) else: print("Request to update bay %s has been accepted." % args.bay)
def split_and_deserialize(string): """Split and try to JSON deserialize a string. Gets a string with the KEY=VALUE format, split it (using '=' as the separator) and try to JSON deserialize the VALUE. :returns: A tuple of (key, value). """ try: key, value = string.split("=", 1) except ValueError: raise exc.CommandError(_('Attributes must be a list of ' 'PATH=VALUE not "%s"') % string) try: value = json.loads(value) except ValueError: pass return (key, value)
def main(self, argv): # NOTE(Christoph Jansen): With Python 3.4 argv somehow becomes a Map. # This hack fixes it. argv = list(argv) # Parse args once to find version and debug settings parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self.setup_debugging(options.debug) # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' # build available subcommands based on version (api_major_version, magnum_api_version) = (self._check_version( options.magnum_api_version)) subcommand_parser = (self.get_subcommand_parser(api_major_version)) self.parser = subcommand_parser if options.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) # Short-circuit and deal with help right away. # NOTE(jamespage): args.func is not guaranteed with python >= 3.4 if not hasattr(args, 'func') or args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if not args.service_type: args.service_type = DEFAULT_SERVICE_TYPE if args.bypass_url: args.os_endpoint_override = args.bypass_url args.os_project_id = (args.os_project_id or args.os_tenant_id) args.os_project_name = (args.os_project_name or args.os_tenant_name) if not cliutils.isunauthenticated(args.func): if (not (args.os_token and (args.os_auth_url or args.os_endpoint_override)) and not args.os_cloud): if not (args.os_username or args.os_user_id): raise exc.CommandError( "You must provide a username via either --os-username " "or via env[OS_USERNAME]") if not args.os_password: raise exc.CommandError( "You must provide a password via either " "--os-password, env[OS_PASSWORD], or prompted " "response") if (not args.os_project_name and not args.os_project_id): raise exc.CommandError( "You must provide a project name or project id via " "--os-project-name, --os-project-id, " "env[OS_PROJECT_NAME] or env[OS_PROJECT_ID]") if not args.os_auth_url: raise exc.CommandError( "You must provide an auth url via either " "--os-auth-url or via env[OS_AUTH_URL]") try: client = { '1': client_v1, }[api_major_version] except KeyError: client = client_v1 args.os_endpoint_type = (args.os_endpoint_type or args.endpoint_type) if args.os_endpoint_type: args.os_interface = args.os_endpoint_type if args.os_interface.endswith('URL'): args.os_interface = args.os_interface[:-3] self.cs = client.Client( cloud=args.os_cloud, user_id=args.os_user_id, username=args.os_username, password=args.os_password, auth_token=args.os_token, project_id=args.os_project_id, project_name=args.os_project_name, user_domain_id=args.os_user_domain_id, user_domain_name=args.os_user_domain_name, project_domain_id=args.os_project_domain_id, project_domain_name=args.os_project_domain_name, auth_url=args.os_auth_url, service_type=args.service_type, region_name=args.os_region_name, magnum_url=args.os_endpoint_override, interface=args.os_interface, insecure=args.insecure, api_version=args.magnum_api_version, ) self._check_deprecation(args.func, argv) args.func(self.cs, args)
def _config_cluster_kubernetes(cluster, cluster_template, cfg_dir, force=False, certs=None, use_keystone=False): """Return and write configuration for the given kubernetes cluster.""" cfg_file = "%s/config" % cfg_dir if cluster_template.tls_disabled or certs is None: cfg = ("apiVersion: v1\n" "clusters:\n" "- cluster:\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " cluster: %(name)s\n" " user: %(name)s\n" " name: %(name)s\n" "current-context: %(name)s\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: %(name)s'\n" % {'name': cluster.name, 'api_address': cluster.api_address}) else: if not use_keystone: cfg = ("apiVersion: v1\n" "clusters:\n" "- cluster:\n" " certificate-authority-data: %(ca)s\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " cluster: %(name)s\n" " user: admin\n" " name: default\n" "current-context: default\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: admin\n" " user:\n" " client-certificate-data: %(cert)s\n" " client-key-data: %(key)s\n" % {'name': cluster.name, 'api_address': cluster.api_address, 'key': base64.encode_as_text(certs['key']), 'cert': base64.encode_as_text(certs['cert']), 'ca': base64.encode_as_text(certs['ca'])}) else: cfg = ("apiVersion: v1\n" "clusters:\n" "- cluster:\n" " certificate-authority-data: %(ca)s\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " cluster: %(name)s\n" " user: openstackuser\n" " name: openstackuser@kubernetes\n" "current-context: openstackuser@kubernetes\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: openstackuser\n" " user:\n" " exec:\n" " command: /bin/bash\n" " apiVersion: client.authentication.k8s.io/v1alpha1\n" " args:\n" " - -c\n" " - >\n" " if [ -z ${OS_TOKEN} ]; then\n" " echo 'Error: Missing OpenStack credential from environment variable $OS_TOKEN' > /dev/stderr\n" # noqa " exit 1\n" " else\n" " echo '{ \"apiVersion\": \"client.authentication.k8s.io/v1alpha1\", \"kind\": \"ExecCredential\", \"status\": { \"token\": \"'\"${OS_TOKEN}\"'\"}}'\n" # noqa " fi\n" % {'name': cluster.name, 'api_address': cluster.api_address, 'ca': base64.encode_as_text(certs['ca'])}) if os.path.exists(cfg_file) and not force: raise exc.CommandError("File %s exists, aborting." % cfg_file) else: f = open(cfg_file, "w") f.write(cfg) f.close() if 'csh' in os.environ['SHELL']: return "setenv KUBECONFIG %s\n" % cfg_file else: return "export KUBECONFIG=%s\n" % cfg_file
def main(self, argv): # NOTE(Christoph Jansen): With Python 3.4 argv somehow becomes a Map. # This hack fixes it. argv = list(argv) # Parse args once to find version and debug settings parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self.setup_debugging(options.debug) # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' subcommand_parser = (self.get_subcommand_parser( options.magnum_api_version)) self.parser = subcommand_parser if options.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) # Short-circuit and deal with help right away. # NOTE(jamespage): args.func is not guaranteed with python >= 3.4 if not hasattr(args, 'func') or args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 (os_username, os_tenant_name, os_tenant_id, os_user_domain_id, os_user_domain_name, os_project_domain_id, os_project_domain_name, os_auth_url, os_auth_system, endpoint_type, service_type, bypass_url, insecure) = ((args.os_username, args.os_tenant_name, args.os_tenant_id, args.os_user_domain_id, args.os_user_domain_name, args.os_project_domain_id, args.os_project_domain_name, args.os_auth_url, args.os_auth_system, args.endpoint_type, args.service_type, args.bypass_url, args.insecure)) if os_auth_system and os_auth_system != "keystone": auth_plugin = auth.load_plugin(os_auth_system) else: auth_plugin = None # Fetched and set later as needed os_password = None if not endpoint_type: endpoint_type = DEFAULT_ENDPOINT_TYPE if not service_type: service_type = DEFAULT_SERVICE_TYPE # NA - there is only one service this CLI accesses # service_type = utils.get_service_type(args.func) or service_type # FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if not cliutils.isunauthenticated(args.func): if auth_plugin: auth_plugin.parse_opts(args) if not auth_plugin or not auth_plugin.opts: if not os_username: raise exc.CommandError("You must provide a username " "via either --os-username or " "env[OS_USERNAME]") if not os_tenant_name and not os_tenant_id: raise exc.CommandError("You must provide a tenant name " "or tenant id via --os-tenant-name, " "--os-tenant-id, env[OS_TENANT_NAME] " "or env[OS_TENANT_ID]") if not os_auth_url: if os_auth_system and os_auth_system != 'keystone': os_auth_url = auth_plugin.get_auth_url() if not os_auth_url: raise exc.CommandError("You must provide an auth url " "via either --os-auth-url or " "env[OS_AUTH_URL] or specify an " "auth_system which defines a " "default url with --os-auth-system " "or env[OS_AUTH_SYSTEM]") # NOTE: The Magnum client authenticates when you create it. So instead of # creating here and authenticating later, which is what the novaclient # does, we just create the client later. # Now check for the password/token of which pieces of the # identifying keyring key can come from the underlying client if not cliutils.isunauthenticated(args.func): # NA - Client can't be used with SecretsHelper if (auth_plugin and auth_plugin.opts and "os_password" not in auth_plugin.opts): use_pw = False else: use_pw = True if use_pw: # Auth using token must have failed or not happened # at all, so now switch to password mode and save # the token when its gotten... using our keyring # saver os_password = args.os_password if not os_password: raise exc.CommandError( 'Expecting a password provided via either ' '--os-password, env[OS_PASSWORD], or ' 'prompted response') try: client = { '1': client_v1, }[options.magnum_api_version] except KeyError: client = client_v1 self.cs = client.Client(username=os_username, api_key=os_password, project_id=os_tenant_id, project_name=os_tenant_name, user_domain_id=os_user_domain_id, user_domain_name=os_user_domain_name, project_domain_id=os_project_domain_id, project_domain_name=os_project_domain_name, auth_url=os_auth_url, service_type=service_type, region_name=args.os_region_name, magnum_url=bypass_url, endpoint_type=endpoint_type, insecure=insecure) args.func(self.cs, args)