def handle_updates(vlab_api, vlab_config, skip_update_check): """Check for an updated CLI, and prompt the user to download if available. :Returns: None :param vlab_api: An established HTTP/S connect to the vLab server :type vlab_api: vlab_cli.lib.api.vLabApi :param vlab_config: The user's config :type vlab_config: configparser.ConfigParser :param skip_update_check: A chicken switch to avoid SPAMing power users :type skip_update_check: Boolean """ if skip_update_check: return page = vlab_api.get('https://vlab.emc.com/getting_started.html').content soup = BeautifulSoup(page, features="html.parser") site_version = '' for a in soup.find_all('a', href=True): url = a['href'] package = os.path.basename(url) if package.startswith('vlab-cli'): # example package name: vlab-cli-2020.5.21-amd64.msi site_version = package.split('-')[2] break current_version = version.__version__ if site_version and site_version != current_version: question = "A new version of vLab CLI is available. Download now? [Y/n]" answer = prompt(question, boolean=True, boolean_default=True) if answer: download_url = build_url(vlab_api.server, url) conn = Connectorizer(vlab_config, gateway_ip='n/a') conn.https(port=443, url=download_url)
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
def _get_version(): """A cheeky interaction to get the correct version of OneFS to create :Returns: String """ new_version_question = "Now, what version would you like?" new_version_ok = "Deploy OneFS {}, correct? [yes/No]" answer = prompt(new_version_question) if answer.strip() == 'vlab show onefs --images': typewriter("When I said \"you can run [that] command in the future\", I meant a much later future ;)") typewriter("Like, outside of me walking you through this OneFS deployment.") ok = False else: ok = prompt(new_version_ok.format(answer), boolean=True) if not ok: answer = prompt_and_confirm(new_version_question, new_version_ok) return answer
def prompt_and_confirm(prompt_msg, confirm_msg): """Ask a question, and confirm the response :Returns: String :param prompt_msg: The question to ask the user. :type prompt_msg: String :param comfirm_msg: The question to ask to ensure the response is OK. :param confirm_msg: String """ prompt_answer = prompt(prompt_msg) answer_ok = prompt(confirm_msg.format(prompt_answer), boolean=True) while not answer_ok: prompt_answer = prompt(prompt_msg) answer_ok = prompt(confirm_msg.format(prompt_answer), boolean=True) return prompt_answer
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)
def _get_ext_ips(): """Ensures the supplied IPs are sane :Returns: Tuple """ question = "With the syntax of --external-ip-range in mind, what range of IPs do you want to use?" answer = prompt(question) sanitized, ok = _check_ip_answer(answer) if not ok: typewriter('Invalid value of {} supplied'.format(answer)) typewriter('Remember, supply two IPv4 addresses.') typewriter('Example: 192.168.1.20 192.168.1.25') while not ok: answer = prompt(question) sanitized, ok = _check_ip_answer(answer) if not ok: typewriter('Invalid value of {} supplied'.format(answer)) return sanitized
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")
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: 'wt' is short for Windows Terminal, which also requires 'ssh' to be installed.") typewriter("Note: 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', '') chrome = found_programs.get('chrome', '') putty = found_programs.get('putty', '') secure_crt = found_programs.get('securecrt', '').lower() windows_term = found_programs.get('wt', '') winscp = found_programs.get('winscp', '').lower() filezilla = found_programs.get('filezilla', '') scp = found_programs.get('scp', '') browsers = [x for x in [firefox, chrome] if x] if firefox and chrome: forget_browsers = which_client(browsers, 'Browser') for browser in forget_browsers: found_programs.pop(browser) scp_clients = [x for x in [winscp, filezilla, scp] if x] if len(scp_clients) > 1: forget_scp = which_client(scp_clients, 'SCP') for scp_client in forget_scp: found_programs.pop(scp_client) ssh_clients = [x for x in [putty, secure_crt, windows_term] if x] if len(ssh_clients) > 1: forget_ssh_clients = which_client(ssh_clients, 'SSH') for ssh_client in forget_ssh_clients: found_programs.pop(ssh_client) 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)
def handle_updates(vlab_api, vlab_config, skip_update_check): """Check for an updated CLI, and prompt the user to download if available. :Returns: None :param vlab_api: An established HTTP/S connect to the vLab server :type vlab_api: vlab_cli.lib.api.vLabApi :param vlab_config: The user's config :type vlab_config: configparser.ConfigParser :param skip_update_check: A chicken switch to avoid SPAMing power users :type skip_update_check: Boolean """ if skip_update_check: return page = vlab_api.get('https://vlab.emc.com/getting_started.html').content soup = BeautifulSoup(page, features="html.parser") site_version = '' for a in soup.find_all('a', href=True): url = a['href'] package = os.path.basename(url) if package.startswith('vlab-cli'): # example package name: vlab-cli-2020.5.21-amd64.msi site_version = package.split('-')[2] break current_version = version.__version__ if site_version and site_version != current_version: question = "A new version of vLab CLI is available. Would you like to install it now? [Y/n]" answer = prompt(question, boolean=True, boolean_default=True) if answer: download_url = build_url(vlab_api.server, url) typewriter("Downloading latest version...") urllib.request.urlretrieve(download_url, r"C:\Windows\Temp\vlab-cli.msi") typewriter( "Installing latest version. Please follow any prompts on the installer." ) # This will launch the install in a non-interactive mode. subprocess.call( r"msiexec.exe /i C:\Windows\Temp\vlab-cli.msi /qn /passive")
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