Example #1
0
def invoke_tutorial():
    keep_going = prompt(
        'Would you like to go over using the vLab CLI? [Yes/no]',
        boolean=True,
        boolean_default=True)
    if not keep_going:
        typewriter(
            "Ok, but don't get angry when I tell you, \"you did it wrong.\" :P\n"
        )
        return
    typewriter('Awesome, I like explaining this sort of thing!\n')

    typewriter(
        'The vLab CLI can be broken down into *commands* and *arguments* (aka flags, aka options).'
    )
    typewriter(
        'An argument always starts with either a dash (-) or a double-dash (--).'
    )
    typewriter('For example, --name is an argument and so is -v\n')

    typewriter('A command just starts with a letter, so if you see no dashes')
    typewriter(
        "just assume it's a command. A command can also have a *subcommand*,")
    typewriter(
        "which is a fancy way to say that \"words can come after words.\"\n")

    typewriter(
        "For example, the command 'vlab init' consist of the base command")
    typewriter("'vlab' followed by the subcommand 'init'.\n")

    typewriter(
        "Now, arguments belong to their specific command (or subcommand).")
    typewriter(
        "This means you cannot mix and match order at the command line.\n")

    typewriter("For instance, an argument of the 'vlab' command is '--verify'")
    typewriter("and the 'init' subcommand has no '--verify' argument.\n")

    typewriter(
        "So if you try to run 'vlab init --verify', I'm going to generate")
    typewriter("an error telling you that \"you did it wrong.\"\n")

    typewriter(
        "Personally, I feel like the hardest part of using a CLI is learning")
    typewriter(
        "\"what's the command for that again...\" (aka command syntax).")
    typewriter(
        "If you remember one thing today, remember that *every command*")
    typewriter("supports the '--help' argument, which will output a little")
    typewriter("help-page noting the valid arguments and subcommands.\n")

    typewriter(
        "I know that using a CLI can be tough at first, especially if you're")
    typewriter(
        "\"from a Windows background\", so don't be surprised if I reach out")
    typewriter("at some point in the future to help with command syntax.\n")
Example #2
0
def ecs(ctx, name, image, external_network, skip_config):
    """Create an instance of Dell EMC Elastic Cloud Storage"""
    body = {'network': external_network, 'name': name, 'image': image}
    resp = consume_task(
        ctx.obj.vlab_api,
        endpoint='/api/2/inf/ecs',
        message='Creating a new instance of ECS running {}'.format(image),
        body=body,
        timeout=1200,
        pause=5)
    data = resp.json()['content'][name]
    ipv4_addrs = get_ipv4_addrs(data['ips'])
    port_mapping = {}
    if ipv4_addrs:
        vm_type = data['meta']['component']
        https_port = https_to_port(vm_type.lower())
        with Spinner('Creating SSH and HTTPS port mapping rules'):
            for ipv4 in ipv4_addrs:
                portmap_payload = {
                    'target_addr': ipv4,
                    'target_port': 22,
                    'target_name': name,
                    'target_component': vm_type
                }
                new_port = ctx.obj.vlab_api.post(
                    '/api/1/ipam/portmap',
                    json=portmap_payload).json()['content']['conn_port']
                port_mapping[ipv4] = new_port
                portmap_payload['target_port'] = https_port
                ctx.obj.vlab_api.post('/api/1/ipam/portmap',
                                      json=portmap_payload)

    if not skip_config:
        resp = consume_task(ctx.obj.vlab_api,
                            endpoint='/api/2/inf/gateway',
                            message='Looking gateway information',
                            method='GET').json()['content']
        gateway_ips = [
            x for x in resp['ips']
            if not x.startswith('192.168.') and not ':' in x
        ]
        if gateway_ips:
            gateway_ip = gateway_ips[0]
        else:
            error = "Unable to determine IP of your vLab gateway. Is it powered on?"
            raise click.ClickException(error)
        ecs_ip = _determine_ip(port_mapping.keys())
        config_payload = {
            'name': name,
            'ssh_port': port_mapping[ecs_ip],
            'gateway_ip': gateway_ip,
            'ecs_ip': ecs_ip
        }
        consume_task(ctx.obj.vlab_api,
                     endpoint='/api/2/inf/ecs/config',
                     message='Configuring your ECS instance',
                     method='POST',
                     body=config_payload,
                     base_endpoint=False,
                     timeout=1800,
                     pause=5)
    output = format_machine_info(ctx.obj.vlab_api, info=data)
    click.echo(output)
    if ipv4_addrs:
        typewriter(
            "\nUse 'vlab connect ecs --name {}' to access your new ECS instance"
            .format(name))
Example #3
0
def invoke_greeting(username):
    typewriter('Hello {}, welcome to'.format(username))
    for line in VLAB_ASCII.split('\n'):
        print(line)
        time.sleep(0.5)
Example #4
0
def invoke_eula():
    typewriter("Before we get started, let's go over the one basic rule with")
    typewriter("using vLab:\n")
    time.sleep(0.6)
    click.secho("\tDon't ruin this for others\n", bold=True)
    time.sleep(1)
    typewriter(
        "It's a simple rule, right? Well, \"they\" (you know, the ominous")
    typewriter("\"they\") say I should provide a bit more detail. So without")
    typewriter("further ado here's a list of things, not limited to, that")
    typewriter("constitutes \"ruining this for others\":\n")
    typewriter("\t* Creating a bot net")
    typewriter("\t* Mining cryptocurrency (ex Bitcoin)")
    typewriter("\t* Running non-work related software in your lab")
    typewriter("\t* Using lab resources for production anything")
    typewriter("\t* Hacking or general maliciousness of any kind\n")
    typewriter(
        "Violating this one very basic rule will result in, but not limited to,"
    )
    typewriter(
        "being banned from using vLab. Please do not be that person who")
    typewriter("\"ruins this for others.\"\n")
    accepts_terms = prompt(
        "Do you understand and agree to follow this one basic rule? [y/N]",
        boolean=True)
    return accepts_terms
Example #5
0
def invoke_config():
    """Initial config setup help"""
    the_os = platform.system().lower()
    typewriter("In order for 'vlab connect' to work, you'll need to have a")
    typewriter("browser, an SSH client, an SCP client and the VMware Remote Client (VMRC) installed.")
    typewriter("Based on your OS, I can use the following:")
    typewriter(", ".join(configurizer.SUPPORTED_PROGS))
    if the_os == 'windows':
        typewriter("\nNote: mstsc is the default RDP client that comes with Windows")
    typewriter('\nIf you do not have the SSH, RDP, SCP and VMRC clients as well as a supported browser')
    typewriter("installed you'll be wasting time by continuing with this config setup.")
    keep_going = prompt("Continue with config setup? [Yes/no]", boolean=True, boolean_default=True)
    if not keep_going:
        raise RuntimeError("vlab connect prerequisites not met")
    with Spinner('Great! Give me a couple of minutes to find those programs'):
        found_programs = configurizer.find_programs()
        firefox = found_programs.get('firefox', False)
        chrome = found_programs.get('chrome', False)
        putty = found_programs.get('putty', False)
        secure_crt = found_programs.get('securecrt', False)
    if firefox and chrome:
        forget_browser = which_browser()
        found_programs.pop(forget_browser)
    if putty and secure_crt:
        forget_ssh = which_ssh()
        found_programs.pop(forget_ssh)
    if len(found_programs) != 5:
        # They are missing some dependency...
        if the_os == 'windows':
            scanned_drive = 'C:\\'
        else:
            scanned_drive = '/ (i.e. root)'
        typewriter("\nUh oh, there's a problem. I wasn't able to find everything under {}.".format(scanned_drive))
        typewriter("Here are the programs I was able to locate:\n\t{}".format(' '.join(found_programs.keys())))
        typewriter("Please install the missing software, then re-run the 'vlab init' command.")
        raise click.ClickException('Missing required dependencies')
    return _make_config(found_programs)
Example #6
0
def invoke_init_done_help():
    typewriter("Woot! Your virtual lab is ready to use!\n")
    typewriter(
        "You can use 'vlab create --help' to find out what you can make.")
    typewriter(
        "Replacing the word 'create' with 'delete' or 'show' in that command")
    typewriter("does what you think it does.\n")
    typewriter("Use the command 'vlab power [on/off/restart]' if you need to")
    typewriter("change the power state of a component in your lab.\n")
    typewriter(
        "If you have any questions, the official vLab docs might have your")
    typewriter("answer, or at least how to contact a human.\n")
    typewriter("Thanks for using vLab! :)\n")
Example #7
0
def invoke_onefs_network_clippy(username, default_gateway, external_netmask, external_ip_range):
    """TODO"""
    typewriter("Hi {}!\n".format(username))
    typewriter("Looks like those network values aren't going to work:")
    typewriter("Gateway: {}".format(default_gateway))
    typewriter("Netmask: {}".format(external_netmask))
    typewriter("IPs: {} to {}\n".format(external_ip_range[0], external_ip_range[1]))
    typewriter("If I didn't bug you now, configuring that cluster would fail and")
    typewriter("you'd be frustrated with the poor person that made this software :P\n")
    if default_gateway == '192.168.1.1':
        typewriter("This looks like a standard deployment, so pick some IPs between:")
        typewriter("{} and {}".format(IP_STATIC_RANGE[0], IP_STATIC_RANGE[1]))
    else:
        typewriter("Hmm... the only reason to use a different default gateway")
        typewriter("is because you've deployed additional networks in your lab")
        typewriter("and have already configured routing...")
        typewriter("Doing that sort of implies that you already have the skills")
        typewriter("to choose some IPs within a subnet.\n")
        typewriter("Well, good luck! I can't provide direction, but I can keep")
        typewriter("bugging you when the IPs are not within the subnet ;)")
    range_ok = False
    while not range_ok:
        external_ip_range = _get_ext_ips()
        if ext_network_ok(default_gateway, external_netmask, external_ip_range):
            range_ok = True
        else:
            typewriter("Yeah, those IPs don't work either")
    return external_ip_range
Example #8
0
def invoke_bad_missing_config(username, vlab_url):
    """Helps a user fix their vlab.ini config file"""
    typewriter("Hi {}, looks like your vLab configuration file has a problem.".format(username))
    typewriter("The file is located at {}".format(configurizer.CONFIG_FILE))
    typewriter("In order to use the 'vlab connect' commands, we'll have to fix it.")
    typewriter("\nYou can manually fix that file by referencing the spec")
    typewriter("in the official documentation located at: {}".format(vlab_url))
    typewriter("Or I can attempt to fix the file right now (by asking you a pile of questions).")
    return prompt("Do you want me to try and fix your config file? [Yes/no]", boolean=True, boolean_default=True)
Example #9
0
def invoke_onefs_clippy(username, cluster_name, version, external_ip_range, node_count, skip_config):
    """Gives some guidance to new(er) users on how to deploy a OneFS cluster

    :Returns: Tuple
    """
    bail = False
    typewriter("\nHi {}! Looks like you're trying to make a OneFS cluster.".format(username))
    sleep(0.5)
    typewriter("To do that, I'm going to need some more information.\n")
    typewriter("You can avoid this prompt in the future by supplying values for the")
    typewriter("--name, --image, and --external-ip-range arguments\n")
    sleep(0.5)
    keep_going = prompt("Do you want me to continue prompting you for this information now? [yes/No]", boolean=True)
    if not keep_going:
        bail = True
        return cluster_name, version, external_ip_range, bail
    typewriter("\nGreat!")
    typewriter("I'll help walk you through the deployment this time.")
    typewriter("Make sure to supply those arguments in the future.")
    typewriter("If you find I keep prompting you for this information,")
    typewriter("you should ask a vLab admin for some help because you're doing it \"the hard way.\"\n")
    if not cluster_name:
        new_cluster_question = "So, what would you like to name your cluster?"
        new_cluster_confirm = "Your new cluster will be named {}, OK? [yes/No]"
        cluster_name = prompt_and_confirm(new_cluster_question, new_cluster_confirm)
    if not version:
        typewriter("\nOK, now I need to know the version of OneFS to create.")
        typewriter("\nProtip: You can list all available versions in the future with the command:", indent=True)
        typewriter("        vlab show onefs --images", indent=True)
        typewriter("\nGenerally speaking, all released versions of OneFS newer than 8.0.0.0")
        typewriter("are available.")
        version = _get_version()
    if (not external_ip_range) and (not skip_config):
        typewriter('\nYour new cluster will need some external IPs configured.')
        typewriter('The syntax for the --external-ip-range argument is:')
        typewriter('--external-ip-range 192.168.1.20 192.168.1.25', indent=True)
        typewriter('just replace those example IPs with the actual ones you want to use.')
        typewriter('Most OneFS clusters have 1 IP for each node, and you are')
        typewriter('deploying {} node(s).'.format(node_count))
        external_ip_range = _get_ext_ips()
    return cluster_name, version, external_ip_range, bail
Example #10
0
def status(ctx):
    """Display general information about your virtual lab"""
    resp = consume_task(ctx.obj.vlab_api,
                        endpoint='/api/1/inf/inventory',
                        message='Collecting information about your inventory',
                        method='GET',
                        timeout=120)
    vm_info = resp.json()['content']
    gateway = vm_info.pop('defaultGateway', None)
    if gateway:
        try:
            # if the gateway is off, it wont have an IP
            gateway_ip = [
                x for x in gateway['ips']
                if ':' not in x and not x.startswith('192.168')
            ][0]
        except IndexError:
            gateway_ip = gateway['state']
    else:
        gateway_ip = 'None'  # so users see the literal word
    with Spinner('Processing inventory records'):
        vm_body = []
        vm_header = [
            'Name', 'IPs', 'Connectable', 'Type', 'Version', 'Powered',
            'Networks'
        ]
        for vm in sorted(vm_info.keys()):
            params = {'name': vm}
            resp = ctx.obj.vlab_api.get('/api/1/ipam/addr',
                                        params=params,
                                        auto_check=False)
            if resp.json()['error'] == None:
                addr_info = resp.json()['content']
            else:
                addr_info = {}
            connectable = addr_info.get(vm, {}).get('routable', 'initializing')
            networks = ','.join(vm_info[vm].get('networks', ['?']))
            kind = vm_info[vm]['meta']['component']
            version = vm_info[vm]['meta']['version']
            power = vm_info[vm]['state'].replace('powered', '')
            ips = '\n'.join(vm_info[vm]['ips'])
            if not ips:
                # fall back to port map rule
                addrs = addr_info.get(vm, {}).get('addr', '')
                ips = '\n'.join(addrs)
            row = [vm, ips, connectable, kind, version, power, networks]
            vm_body.append(row)

        quota_info = ctx.obj.vlab_api.get('/api/1/quota').json()['content']

    heading = '\nUsername: {}\nGateway : {}\nVM Quota: {}\nVM Count: {}'.format(
        ctx.obj.username, gateway_ip, quota_info['soft-limit'],
        len(vm_info.keys()))
    vm_table = tabulate(vm_body, headers=vm_header, tablefmt='presto')
    click.echo(heading)
    if len(vm_info.keys()) > quota_info['soft-limit']:
        click.secho('\n\t!!!WARNING!!! Currently exceeding VM quota limit!\n',
                    bold=True)
    if quota_info['exceeded_on']:
        exp_date = quota_info['exceeded_on'] + quota_info['grace_period']
        quota_warning = '\tQuota Exceeded on: {}\n'.format(
            to_timestamp(quota_info['exceeded_on']))
        quota_warning += '\tAutomatic VM deletion will occur on: {}\n'.format(
            to_timestamp(exp_date))
        click.secho(quota_warning, bold=True)

    if vm_body:
        click.echo('\nMachines:\n\n{}\n'.format(vm_table))
    else:
        typewriter("Looks like there's nothing in your lab.")
        typewriter("Use 'vlab create -h' to start deploying some machines")
Example #11
0
def onefs(ctx, name, image, node_count, external, internal, external_ip_range,
          internal_ip_range, default_gateway, smartconnect_ip, sc_zonename, dns_servers,
          encoding, external_netmask, internal_netmask, ram, cpu_count, skip_config, compliance):
    """Create a vOneFS cluster. You will be prompted for any missing required parameters."""
    if node_count > 6:
        raise click.ClickException('You can only deploy a maximum of 6 nodes at a time')
    ram = int(ram) # Click only supports strings, but we want a number
    cpu_count = int(cpu_count)
    if ram > 6 and node_count > 1:
        raise click.ClickException('Only single-node clusters can be created with more than 6 GB of RAM')
    tasks = {}
    if skip_config and (name and image):
        bail = False
    elif not (name and image and external_ip_range):
        name, image, external_ip_range, bail = invoke_onefs_clippy(ctx.obj.username,
                                                                   name,
                                                                   image,
                                                                   external_ip_range,
                                                                   node_count,
                                                                   skip_config)
    else:
        bail = False
        low_ip = str(min([ipaddress.ip_address(x) for x in external_ip_range]))
        high_ip = str(max([ipaddress.ip_address(x) for x in external_ip_range]))
        if '192.168.1.1' in _generate_ips(low_ip, high_ip):
            error = 'IP 192.168.1.1 is reserved for the gateway. Unable to assign to OneFS external network'
            raise click.ClickException(error)
    if bail:
        raise click.ClickException('Not enough information supplied')
    if not skip_config:
        ips_ok = ext_network_ok(default_gateway, external_netmask, external_ip_range)
        if not ips_ok:
            external_ip_range = invoke_onefs_network_clippy(ctx.obj.username, default_gateway, external_netmask, external_ip_range)
    name_ok(name)
    info = create_nodes(username=ctx.obj.username,
                        name=name,
                        image=image,
                        node_count=node_count,
                        external=external,
                        internal=internal,
                        ram=ram,
                        cpu_count=cpu_count,
                        vlab_api=ctx.obj.vlab_api)
    if not skip_config:
        config_nodes(cluster_name=name,
                     nodes=info.keys(),
                     image=image,
                     external_ip_range=external_ip_range,
                     internal_ip_range=internal_ip_range,
                     default_gateway=default_gateway,
                     smartconnect_ip=smartconnect_ip,
                     sc_zonename=sc_zonename,
                     dns_servers=dns_servers,
                     encoding=encoding,
                     external_netmask=external_netmask,
                     internal_netmask=internal_netmask,
                     compliance=compliance,
                     vlab_api=ctx.obj.vlab_api)
        map_ips(vlab_api=ctx.obj.vlab_api, nodes=_sort_node_names(info.keys()), ip_range=external_ip_range)
    table = generate_table(vlab_api=ctx.obj.vlab_api, info=info)
    click.echo('\n{}\n'.format(table))
    if not skip_config:
        typewriter("Use 'vlab connect onefs --name {}' to connect to a specific node".format(list(info.keys())[0]))
Example #12
0
def invoke_portmap_clippy(username, vm_type, available_protocols):
    """Help the human choose a protocol when creating/deleting a port mapping/fowarding rule.

    :Returns: String

    :param username: The human that needs some assistance
    :type username: String

    :param vm_type: The category of vLab component (i.e. OneFS, InsightIQ, etc)
    :type vm_type: String

    :param available_protocols: The protocols supported for the given vm_type
    :type available_protocols: List
    """
    typewriter(
        "Hi {}, looks like you forgot or provided an invalid protocol".format(
            username))
    typewriter("of a port mapping/forwarding rule.\n")
    typewriter(
        "Honestly, I don't blame you; I have to look it up which protocols")
    typewriter("are supported by the different components, and I'm a robot!\n")
    typewriter("Looks like the {} component supports these protocols:".format(
        vm_type))
    typewriter("{}".format(' '.join(available_protocols)))
    choice_question = "\nWhich protocol do you want to use?"
    confirm_question = "Ok, use {}? [yes/No]"
    answer = prompt_and_confirm(choice_question, confirm_question)
    if not answer in available_protocols:
        typewriter('\nerror, does not compute...', newline=False)
        typewriter('\rEhh, I mean that is not a valid option')
        typewriter('Remember your options are:')
        typewriter("{}".format(' '.join(available_protocols)))
        while not answer in available_protocols:
            answer = prompt_and_confirm(choice_question, confirm_question)
    return answer
Example #13
0
def install_tab_complete_config(for_shell):
    shell, path = click_completion.core.install(shell=for_shell)
    typewriter('Configured tab-completion for {}'.format(shell))
    typewriter('The config file is located at: {}'.format(path))
    typewriter('For tab-completion to work you MUST open a new {} shell'.format(shell))