from __future__ import print_function
import getpass
import json
import os
import os.path
from builtins import input
from cloudmesh_client.cloud.register import CloudRegister, Register
from cloudmesh_client.common.ConfigDict import Config, ConfigDict
from cloudmesh_client.common.Error import Error
from cloudmesh_client.common.Printer import Printer
from cloudmesh_client.common.util import path_expand
from cloudmesh_client.common.util import yn_choice
from cloudmesh_client.shell.command import PluginCommand, CloudPluginCommand
from cloudmesh_client.shell.command import command
from cloudmesh_client.shell.console import Console
from cloudmesh_client.default import Default
from cloudmesh_client.common.dotdict import dotdict
from stat import *
from cloudmesh_client.common.ssh_config import ssh_config
from cloudmesh_client.common.util import backup_name
from shutil import copy
import getpass
# noinspection PyBroadException
[docs]class RegisterCommand(PluginCommand, CloudPluginCommand):
topics = {"register": "cloud"}
def __init__(self, context):
# super(self.__class__, self).__init__()
self.context = context
if self.context.debug:
print("init command register")
# noinspection PyUnusedLocal
@command
def do_register(self, args, arguments):
"""
::
Usage:
register info
register backup
register new [--force] [--dryrun]
register clean [--force]
register list ssh [--format=FORMAT]
register list [--yaml=FILENAME][--info][--format=FORMAT]
register cat [--yaml=FILENAME]
register edit [--yaml=FILENAME]
register user [USERNAME]
register cloud [CLOUD] [--force]
register remote [CLOUD] [--force]
register export HOST [--password] [--format=FORMAT]
register source HOST
register merge FILEPATH
register form [--yaml=FILENAME]
register check [--yaml=FILENAME]
register test [--yaml=FILENAME]
register json HOST
register env [--provider=PROVIDER]
register ec2 CLOUD EC2ZIP
register ENTRY
managing the registered clouds in the cloudmesh.yaml file.
It looks for it in the current directory, and than in
~/.cloudmesh. If the file with the cloudmesh.yaml name is
there it will use it. If neither location has one a new
file will be created in ~/.cloudmesh/cloudmesh.yaml. Some
defaults will be provided. However you will still need to
fill it out with valid entries.
Arguments:
HOST the host name
USER the user name
FILEPATH the path of the file
CLOUD the cloud name
PROVIDER the provider or type of cloud [Default: openstack]
USERNAME Username that would be registered in yaml. Defaults to OS username.
Options:
--provider=PROVIDER Provider to be used for cloud. Values are:
openstack, azure, ec2.
--version=VERSION Version of the openstack cloud.
--openrc=OPENRC The location of the openrc file
--password Prints the password
--force ignore interactive questions and execute
the action
Description:
register info
lists the clouds specified in the cloudmesh.yaml
file in the current directory, and then in ~/.cloudmesh
register list [--yaml=FILENAME] [--name] [--info]
lists the clouds specified in the cloudmesh.yaml file. If
info is specified it also prints the location of the yaml
file.
register list ssh
lists hosts from ~/.ssh/config
register cat [--yaml=FILENAME]
outputs the cloudmesh.yaml file
register edit [--yaml=FILENAME]
edits the cloudmesh.yaml file
register export HOST [--format=FORMAT]
prints the contents of an openrc.sh file based on the
information found in the cloudmesh.yaml file.
register remote CLOUD [--force]
reads the Openstack OPENRC file from a remote host that
is described in cloudmesh.yaml file. We assume that
the file has already a template for this host. If
not it can be created from other examples before
you run this command.
It uses the OS_OPENRC variable to locate the file and
copy it onto your computer.
register merge FILENAME
Replaces the TBD in cloudmesh.yaml with the contents
present in the named file
register form [--yaml=FILENAME]
interactively fills out the form wherever we find TBD.
register check [--yaml=FILENAME]
checks the yaml file for completness
register test [--yaml=FILENAME]
checks the yaml file and executes tests to check if
we can use the cloud. TODO: maybe this should be in
a test command
register json host
displays the host details in json format
register remote CLOUD
registers a remote cloud and copies the openrc file
specified in the credentials of the cloudmesh.yaml
register CLOUD --dir
Copies the entire directory from the cloud and puts it in
~/.cloudmesh/clouds/host
For kilo, The directory would be copied to
~/.cloudmesh/clouds/kilo
register env [--provider=PROVIDER] [HOSTNAME]
Reads env OS_* variables and registers a new cloud in yaml,
interactively. Default PROVIDER is openstack and HOSTNAME
is localhost.
register user [USERNAME]
Sets the user in yaml with the value provided.
"""
# from pprint import pprint
# pprint(arguments)
def _get_config_yaml_file(arguments):
filename = arguments["--yaml"] or "cloudmesh.yaml"
filename = Config.find_file(filename)
return filename
def exists(filename):
return os.path.isfile(filename)
def export(host, output):
config = ConfigDict("cloudmesh.yaml")
credentials = dict(
config["cloudmesh"]["clouds"][host]["credentials"])
if not arguments["--password"]:
credentials["OS_PASSWORD"] = "********"
if output is None:
for attribute, value in credentials.items():
print("export {}={}".format(attribute, value))
elif output == "table":
print(Printer.attribute(credentials))
else:
print(Printer.write(credentials, output=output))
# TODO: bug csv does not work
if arguments["info"]:
filename = _get_config_yaml_file(arguments)
if os.path.isfile(filename):
Console.ok("File '{}' exists. ok.".format(filename))
Console.ok("The yaml file contains the following templates:")
d = CloudRegister.list(filename,
Default.cloud,
info=False,
output="table")
print(d)
else:
Console.error("File {} does not exist".format(filename))
return ""
elif arguments["backup"]:
name = backup_name("~/.cloudmesh/cloudmesh.yaml")
configfile = path_expand("~/.cloudmesh/cloudmesh.yaml")
print (name)
try:
copy(configfile, name)
Console.ok("Bakup copy created: {}. ok.".format(name))
except:
Console.error("Could not create a backup copy from {}".format(configfile))
return ""
elif arguments["new"]:
import shutil
import cloudmesh_client.etc
config = ConfigDict("cloudmesh.yaml")
data = dotdict({
'dir': cloudmesh_client.etc.__file__,
'filename': os.path.join(
os.path.dirname(cloudmesh_client.etc.__file__),
"cloudmesh.yaml"),
'yamlfile': path_expand("~/.cloudmesh/cloudmesh.yaml"),
'dryrun': arguments['--dryrun']
})
Console.ok(data.filename)
force = arguments["--force"]
if not force:
force = yn_choice("Would you like create a new configuration file at {}".format(data.yamlfile))
if force:
if not data.dryrun:
config.make_a_copy(location=data.yamlfile)
shutil.copyfile(data.filename, data.yamlfile)
print("copy ")
print("From: ", data.filename)
print("To: ", data.yamlfile)
# filename = _get_config_yaml_file(arguments)
# if _exists(filename):
# Console.ok("File '{}' exists. ok.".format(filename))
# else:
# Console.error("File {} does not exist".format(filename))
return ""
elif arguments["clean"]:
filename = _get_config_yaml_file(arguments)
force = arguments["--force"] or False
if filename is not None:
print(filename, force)
if exists(filename):
print("Delete cloudmesh.yaml file:", filename)
if not force:
force = yn_choice("Would you like to delete the "
"cloudmesh.yaml file")
print(force)
if force:
os.remove(filename)
Console.ok("Deleted the file " + filename + ". ok.")
else:
Console.ok("Please use Y to delete the file.")
pass
else:
Console.error("File {} does not exist".format(filename))
else:
Console.error("No cloudmesh.yaml file found.")
return ""
elif arguments["cat"]:
filename = _get_config_yaml_file(arguments)
if exists(filename):
with open(filename, 'r') as f:
lines = f.read().split("\n")
print('\n'.join(lines))
else:
Console.error("File {} does not exist".format(filename))
return ""
elif arguments["edit"]:
filename = _get_config_yaml_file(arguments)
if exists(filename):
try:
data = {"editor": os.environ["EDITOR"],
"filename": filename}
Console.ok("editing file " + filename)
os.system("{editor} {filename}".format(**data))
except:
Console.error("No operating system environment variable EDITOR set.", traceflag=False)
else:
Console.error("File {} does not exist".format(filename), traceflag=False)
return ""
elif arguments['list'] and arguments['ssh']:
output = arguments['--format'] or 'table'
hosts = CloudRegister.list_ssh()
print(Printer.list(hosts, output=output))
return ""
elif arguments['list']:
filename = _get_config_yaml_file(arguments)
info = arguments["--info"] or False
output = arguments["--format"] or "table"
if not filename:
Console.error("File {} doesn't exist".format(filename))
else:
d = CloudRegister.list(filename,
Default.cloud,
info=info,
output=output)
print(d)
return ""
elif arguments['check']:
filename = _get_config_yaml_file(arguments)
if not filename:
Console.error("File {} doesn't exist".format(
arguments["--yaml"] or 'cloudmesh.yaml'))
else:
CloudRegister.check_yaml_for_completeness(filename)
return ""
elif arguments['merge']:
filename = arguments['FILENAME']
CloudRegister.from_file(filename)
return ""
elif arguments['test']:
filename = _get_config_yaml_file(arguments)
CloudRegister.test(filename)
return ""
elif arguments['form']:
filename = _get_config_yaml_file(arguments)
if not filename:
Console.error("File {} doesn't exist".format(
arguments["--yaml"] or 'cloudmesh.yaml'))
else:
CloudRegister.fill_out_form(filename)
return ""
elif arguments['source']:
host = arguments['HOST']
config = ConfigDict("cloudmesh.yaml")
credentials = dict(
config["cloudmesh"]["clouds"][host]["credentials"])
# unset
variables = list(os.environ)
for attribute in variables:
if attribute.startswith("OS_"):
print("x ", attribute)
del os.environ[attribute]
# set
for attribute, value in credentials.items():
os.putenv(attribute, value)
print("+ ", attribute)
export(host, "table")
return ""
elif arguments['export']:
output = arguments['--format']
host = arguments['HOST']
try:
variables = list(os.environ)
for attribute in variables:
if attribute.startswith("OS_"):
print("unset ", attribute)
del os.environ[attribute]
export(host, output)
except:
Console.error ("The export may not include all values", traceflag=False)
return ""
elif arguments['json']:
host = arguments['HOST']
result = CloudRegister.get(host)
if result:
print(json.dumps(result, indent=4))
else:
print("Cloud {:} is not described in cloudmesh.yaml".format(
host))
return ""
elif arguments['remote']:
force = arguments['--force']
cloud = arguments['CLOUD']
if cloud is None:
# clouds = [ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["active"][0]]
clouds = ["kilo"] # hardcode to kilo for now
else:
clouds = [cloud]
for cloud in clouds:
CloudRegister.remote(cloud, force)
export(cloud, "table")
config = ConfigDict("cloudmesh.yaml")
if config["cloudmesh.profile.user"] == "TBD":
name = config["cloudmesh.clouds.kilo.credentials.OS_USERNAME"]
config["cloudmesh"]["profile"]["user"] = name
config.save()
return ""
elif arguments['ec2']:
cloud = arguments['CLOUD']
zipfile = arguments['EC2ZIP']
if cloud is None:
clouds = [ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["active"][0]]
else:
clouds = [cloud]
for cloud in clouds:
CloudRegister.ec2(cloud, zipfile)
export(cloud, "table")
return ""
elif arguments['env']:
try:
CloudRegister.from_environ(arguments['--provider'])
except Exception as e:
Error.traceback(e)
return ""
elif arguments['cloud']:
"""
if arguments['--dir']:
cloud = arguments['--name']
directory = arguments['--dir']
Console.ok(directory)
CloudRegister.directory(cloud, directory)
else:
"""
values_to_replace = ['tbd', 'null', 'tbd_not_used']
cloud = arguments['CLOUD']
if cloud is None:
clouds = [ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["active"][0]]
else:
clouds = [cloud]
for cloud in clouds:
config = ConfigDict("cloudmesh.yaml")
cloud_config = config["cloudmesh.clouds"][cloud]
# Checking credentials
print("Checking cloud credentials...")
for prop in cloud_config["credentials"]:
if cloud_config["credentials"][prop].lower() in values_to_replace:
value = input(prop + "(" + cloud_config["credentials"][prop] + "): ")
cloud_config["credentials"][prop] = value
# Checking defaults
print("Checking cloud defaults...")
for prop in cloud_config["default"]:
if cloud_config["default"][prop].lower() in values_to_replace:
value = input(prop + "(" + cloud_config["default"][prop] + "): ")
cloud_config["default"][prop] = value
config.save()
export(cloud, "table")
return ""
elif arguments['user']:
username = arguments["USERNAME"] or getpass.getuser()
CloudRegister.set_username(username)
Console.ok("Setting profile user to {} in the yaml file.".format(username))
hosts = ssh_config()
hosts.generate(key="india", username=username, verbose=True)
return ""
elif arguments['ENTRY'].lower() in ['chameleon']:
config = ConfigDict("cloudmesh.yaml")
credentials = dotdict(config["cloudmesh.clouds.chameleon.credentials"])
default = credentials.OS_USERNAME
username = input("Please enter the username for {:} [{}]: ".format("chameleon",
default))
username = username or default
while True:
default = credentials.OS_PROJECT_NAME
project = input("Please enter the project id for {:} [{}]: ".format("chameleon",
default))
project = project or default
if project.isdigit():
project = "CH-{}".format(project)
break
else:
try:
prefix, number = project.split("-")
if not (prefix in ["CH"] and number.isdigit()):
Console.error("This is not a valid Chameleon.org cloud project", traceflag=False)
else:
break
except:
Console.error("This is not a valid Chameleon.org cloud project", traceflag=False)
password = getpass.getpass("Please enter the password for {:}: ".format("chameleon", credentials.OS_PASSWORD))
credentials.OS_TENENT_ID = credentials.OS_PROJECT_NAME
credentials.OS_TENENT_NAME = credentials.OS_PROJECT_NAME
credentials.OS_USERNAME = username
credentials.OS_PASSWORD = password
config.save()
return ""
elif arguments['ENTRY'] is not None:
name = arguments['ENTRY']
Register.entry(name)
return ""
# if all fails do a simple list
filename = _get_config_yaml_file(arguments)
CloudRegister.list(filename)
pass