def _maybe_glob_deps(deployment_id): matching_deployments = None if _is_a_glob(deployment_id): deps = Deployment.list(True) dep_ids = [d.dep_id for d in deps] matching_dep_ids = fnmatch.filter(dep_ids, deployment_id) matching_deployments = [ d for d in deps if d.dep_id in matching_dep_ids ] else: matching_deployments = [Deployment.load(deployment_id)] return matching_deployments
def tunnel(deployment_id, service=None, node=None, remote_port=None, local_port=None, local_address=None): """ Creates an SSH port forwarding for the services that are running in the deployment DEPLOYMENT_ID. If SERVICE is not specified, you can use the --remote-port and --node to forward a generic service. """ dep = Deployment.load(deployment_id) if service: if service == 'dashboard': click.echo( "Opening tunnel to service 'dashboard' in deployment '{}'...". format(dep.dep_id)) else: click.echo("Opening tunnel to service '{}' on node '{}' of " "deployment '{}'...".format(service, node, dep.dep_id)) elif remote_port: click.echo( "Opening tunnel between remote {} port and local {} port on " "node '{}' of deployment '{}'...".format( remote_port, local_port if local_port else remote_port, node, dep.dep_id)) dep.start_port_forwarding(service, node, remote_port, local_port, local_address)
def scp(recursive, deployment_id, source, destination): """ Prepares and executes a 'scp' command to copy a file or entire directory from a cluster node to the host, or from the host to a cluster node. Takes three arguments: in addition to deployment_id, it needs both a source and destination, one of which will be in a special form: <node>:<path> Note: You can check the existing node names with the command "sesdev show <deployment_id>" For example, to copy the file /etc/os-release from the node 'master' on cluster (deployment_id) 'foo' to the current directory on the host: sesdev scp foo master:/etc/os-release . To recursively copy the entire directory "/bar" from the host to "/root/bar" on node1 in deployment foo: sesdev scp --recursive foo /bar node1: (From the cluster node's perspective, the scp operation will be done as root.) The --recursive option can also be abbreviated to -r and can appear anywhere in the command line, e.g.: sesdev scp foo -r /bar node1: """ dep = Deployment.load(deployment_id) dep.scp(source, destination, recurse=recursive)
def _create_command(deployment_id, deploy, settings_dict): interactive = not settings_dict.get('non_interactive', False) settings = Settings(**settings_dict) dep = Deployment.create(deployment_id, settings) if not dep.settings.devel_repo: if dep.settings.version not in Constant.CORE_VERSIONS: raise OptionNotSupportedInVersion('--product', dep.settings.version) really_want_to = None click.echo( "=== Creating deployment \"{}\" with the following configuration ===". format(deployment_id)) click.echo(dep.status()) if deploy: really_want_to = True if interactive: really_want_to = click.confirm( 'Do you want to continue with the deployment?', default=True, ) try: if really_want_to: dep.vet_configuration() if dep.settings.dry_run: click.echo( "Dry run. Stopping now, before creating any VMs.") raise click.Abort() dep.start(_print_log) click.echo("=== Deployment Finished ===") click.echo() click.echo("You can login into the cluster with:") click.echo() click.echo(" $ sesdev ssh {}".format(deployment_id)) click.echo() if dep.settings.version == 'ses5': click.echo("Or, access openATTIC with:") click.echo() click.echo( " $ sesdev tunnel {} openattic".format(deployment_id)) elif dep.settings.version == 'octopus' and dep.has_suma(): click.echo("Or, access the SUMA WebUI with:") click.echo() click.echo( " $ sesdev tunnel {} suma".format(deployment_id)) click.echo() else: click.echo("Or, access the Ceph Dashboard with:") click.echo() click.echo( " $ sesdev tunnel {} dashboard".format(deployment_id)) click.echo() else: raise click.Abort() except click.Abort: click.echo() click.echo("Exiting...") dep.destroy(_silent_log)
def add_repo(update, deployment_id, custom_repo): """ Add a custom repo to all nodes of an already-deployed cluster. The repo should be specified in the form of an URL, but it is optional: if it is omitted, the "devel" repo (which has a specific meaning depending on the deployment version) will be added. """ dep = Deployment.load(deployment_id) dep.add_repo_subcommand(custom_repo, update, _print_log)
def replace_mgr_modules(deployment_id, **kwargs): """ Fetches a different version of Ceph MGR modules from a local repository or github, replacing the installed ones. --local, --pr and --branch conflict with each other, when the first is found the remaining are ignored. """ dep = Deployment.load(deployment_id) dep.replace_mgr_modules(**kwargs)
def redeploy(deployment_id, **kwargs): """ Destroys the VMs of the deployment DEPLOYMENT_ID and deploys again the cluster from scratch with the same configuration. """ interactive = not (kwargs.get('non_interactive', False) or kwargs.get('force', False)) if interactive: really_want_to = True if interactive: really_want_to = click.confirm( 'Do you want to continue with the deployment?', default=True, ) if not really_want_to: raise click.Abort() dep = Deployment.load(deployment_id) dep.destroy(_print_log) dep = Deployment.create(deployment_id, dep.settings) dep.start(_print_log)
def supportconfig(deployment_id, node): """ Runs supportconfig on a node within an already-deployed cluster. Dumps the resulting tarball in the current working directory. If the node is not specified, it defaults to "master". NOTE: supportconfig is only available in deployments running on SUSE Linux Enterprise. """ dep = Deployment.load(deployment_id) _node = 'master' if node is None else node dep.supportconfig(_print_log, _node)
def remove_box(box_name, **kwargs): """ Remove a Vagrant Box installed in the system by sesdev. This involves first removing the corresponding image from the libvirt storage pool, and then running 'vagrant box remove' on it. """ settings_dict = _gen_box_settings_dict(**kwargs) settings = Settings(**settings_dict) # # existing deployments might be using this box deps = Deployment.list(True) existing_deployments = [] for dep in deps: if box_name == dep.settings.os: existing_deployments.append(dep.dep_id) if existing_deployments: if len(existing_deployments) == 1: click.echo( "The following deployment is already using box ->{}<-:".format( box_name)) else: click.echo( "The following deployments are already using box ->{}<-:". format(box_name)) for dep_id in existing_deployments: click.echo(" {}".format(dep_id)) click.echo() if len(existing_deployments) == 1: click.echo("It must be destroyed first!") else: click.echo("These must be destroyed first!") sys.exit(-1) box_obj = Box(settings) if box_obj.exists(box_name): click.echo("Proceeding to remove Vagrant Box ->{}<-".format(box_name)) else: click.echo("There is no Vagrant Box called ->{}<-".format(box_name)) sys.exit(-1) image_to_remove = box_obj.get_image_by_box(box_name) if image_to_remove: click.echo("Found related image ->{}<- in libvirt storage pool".format( image_to_remove)) box_obj.remove_image(image_to_remove) click.echo("Libvirt image removed.") box_obj.remove_box(box_name) click.echo("Vagrant Box removed.")
def ssh(deployment_id, node=None, command=None): """ Opens an SSH shell to, or runs optional COMMAND on, node NODE in deployment DEPLOYMENT_ID. If the node is not specified, it defaults to "master". Note: You can check the existing node names with the command "sesdev show <deployment_id>" """ dep = Deployment.load(deployment_id) node_name = 'master' if node is None else node if command: log_msg = "Running SSH command on {}: {}".format(node_name, command) logger.info(log_msg) dep.ssh(node_name, command)
def box_remove_handler(box_name, **kwargs): settings_dict = _gen_box_settings_dict(**kwargs) interactive = not settings_dict.get('non_interactive', False) settings = Settings(**settings_dict) box_obj = Box(settings) boxes_to_remove = box_obj.maybe_glob_boxes(box_name, simple=True) box_word = None if boxes_to_remove: box_word = _singular_or_plural(boxes_to_remove) else: return None if interactive: if boxes_to_remove: click.echo( "You have asked to remove the following {}".format(box_word)) click.echo("---------------------------------------" + len(box_word) * '-') for box_to_remove in boxes_to_remove: click.echo(box_to_remove) click.echo() really_want_to = click.confirm( 'Do you really want to remove {} {}'.format( len(boxes_to_remove), box_word), default=True, ) if not really_want_to: raise click.Abort() # # remove the boxes deps = Deployment.list(True) problems_encountered = False boxes_removed_count = 0 for box_being_removed in boxes_to_remove: Log.info("Attempting to remove Vagrant Box ->{}<- ...".format( box_being_removed)) # # existing deployments might be using this box existing_deployments = [] for dep in deps: if box_being_removed == dep.settings.os: existing_deployments.append(dep.dep_id) if existing_deployments: if len(existing_deployments) == 1: Log.warning( "The following deployment is already using Vagrant Box ->{}<-:" .format(box_being_removed)) else: Log.warning( "The following deployments are already using Vagrant Box ->{}<-:" .format(box_being_removed)) for dep_id in existing_deployments: Log.warning(" {}".format(dep_id)) click.echo() if len(existing_deployments) == 1: Log.warning("It must be destroyed first!") else: Log.warning("These must be destroyed first!") problems_encountered = True continue image_to_remove = box_obj.get_image_by_box(box_being_removed) if image_to_remove: Log.info( "Found related image ->{}<- in libvirt storage pool".format( image_to_remove)) box_obj.remove_image(image_to_remove) Log.info("Libvirt image removed.") box_obj.remove_box(box_being_removed) Log.info("Vagrant Box ->{}<- removed.".format(box_being_removed)) boxes_removed_count += 1 if boxes_removed_count != 1: click.echo("{} Vagrant Boxes were removed".format(boxes_removed_count)) if problems_encountered: Log.warning("sesdev tried to remove {} Vagrant Box(es), but " "problems were encountered.".format(len(boxes_to_remove))) click.echo("Use \"sesdev box list\" to check current state") return None
def list_deps(format_opt): """ Lists all the available deployments. """ p_table = None deployments_list = [] deps = Deployment.list(True) if deps: log_msg = "Found deployments: {}".format(", ".join(d.dep_id for d in deps)) logger.info(log_msg) else: msg = "No deployments found" logger.info(msg) if format_opt in ['json']: click.echo(json.dumps([], sort_keys=True, indent=4)) else: click.echo(msg) return None def _status(nodes): status = None for node in nodes.values(): if status is None: status = node.status elif node.status == 'running' and status == 'not deployed': status = 'partially deployed' elif node.status == 'stopped' and status == 'running': status = 'partially running' elif node.status == 'suspended' and status == 'running': status = 'partially running' elif node.status == 'running' and status == 'stopped': status = 'partially running' elif node.status == 'running' and status == 'suspended': status = 'partially running' return status if format_opt not in ['json']: p_table = PrettyTable(["ID", "Version", "Status", "Nodes"]) p_table.align = "l" for dep in deps: logger.debug("Looping over deployments: %s", dep.dep_id) status = str(_status(dep.nodes)) logger.debug("-> status: %s", status) version = getattr(dep.settings, 'version', None) logger.debug("-> version: %s", version) nodes = getattr(dep, 'nodes', None) node_names = '(unknown)' if nodes is None else ', '.join(nodes) logger.debug("-> node_names: %s", node_names) if format_opt in ['json']: deployments_list.append({ "id": dep.dep_id, "version": version, "status": status, "nodes": list(nodes), }) else: p_table.add_row([dep.dep_id, version, status, node_names]) if format_opt in ['json']: click.echo(json.dumps(deployments_list, sort_keys=True, indent=4)) else: deployment_word = "deployments" if len(deps) > 1 else "deployment" click.echo("Found {} {}:".format(len(deps), deployment_word)) click.echo() click.echo(p_table) click.echo()
def replace_ceph_salt(deployment_id, local=None): """ Install ceph-salt from source """ dep = Deployment.load(deployment_id) dep.replace_ceph_salt(local)
def show(deployment_id): """ Shows the information of deployment DEPLOYMENT_ID """ dep = Deployment.load(deployment_id) click.echo(dep.status())
def qa_test(deployment_id): """ Runs QA test on an already-deployed cluster. """ dep = Deployment.load(deployment_id) dep.qa_test(_print_log)