from cloudmesh_client.shell.console import Console
from cloudmesh_client.shell.command import command, PluginCommand, CometPluginCommand
from cloudmesh_client.comet.comet import Comet
from cloudmesh_client.comet.cluster import Cluster
from cloudmesh_client.common.hostlist import Parameter
from cloudmesh_client.common.ConfigDict import ConfigDict
import hostlist
import os
import sys
from builtins import input
from pprint import pprint
from cloudmesh_client.common.Printer import Printer
import requests
# noinspection PyUnusedLocal,PyBroadException
[docs]class CometCommand(PluginCommand, CometPluginCommand):
topics = {"comet": "comet"}
def __init__(self, context):
self.context = context
self.context.comet_token = None
if self.context.debug:
Console.ok("init comet command")
@command
def do_comet(self, args, arguments):
"""
::
Usage:
comet init
comet active [ENDPOINT]
comet ll [CLUSTERID] [--format=FORMAT] [--endpoint=ENDPOINT]
comet cluster [--concise|--status] [CLUSTERID]
[--format=FORMAT]
[--sort=SORTKEY]
[--endpoint=ENDPOINT]
comet computeset [COMPUTESETID]
[--allocation=ALLOCATION]
[--cluster=CLUSTERID]
[--state=COMPUTESESTATE]
[--endpoint=ENDPOINT]
comet start CLUSTERID [--count=NUMNODES] [COMPUTENODEIDS]
[--allocation=ALLOCATION]
[--reservation=RESERVATION]
[--walltime=WALLTIME]
[--endpoint=ENDPOINT]
comet terminate COMPUTESETID [--endpoint=ENDPOINT]
comet power (on|off|reboot|reset|shutdown) CLUSTERID [NODESPARAM]
[--endpoint=ENDPOINT]
comet console [--link] CLUSTERID [COMPUTENODEID]
[--endpoint=ENDPOINT]
comet node info CLUSTERID [COMPUTENODEID] [--format=FORMAT]
[--endpoint=ENDPOINT]
comet node rename CLUSTERID OLDNAMES NEWNAMES
[--endpoint=ENDPOINT]
comet iso list [--endpoint=ENDPOINT]
comet iso upload [--isoname=ISONAME] PATHISOFILE
[--endpoint=ENDPOINT]
comet iso attach ISOIDNAME CLUSTERID [COMPUTENODEIDS]
[--endpoint=ENDPOINT]
comet iso detach CLUSTERID [COMPUTENODEIDS]
[--endpoint=ENDPOINT]
comet reservation (list|create|update|delete)
Options:
--endpoint=ENDPOINT Specify the comet nucleus service
endpoint to work with, e.g., dev
or production
--format=FORMAT Format is either table, json, yaml,
csv, rest
[default: table]
--sort=SORTKEY Sorting key for the table view
--count=NUMNODES Number of nodes to be powered on.
When this option is used, the comet system
will find a NUMNODES number of arbitrary nodes
that are available to boot as a computeset
--allocation=ALLOCATION Allocation to charge when power on
node(s)
--reservation=RESERVATION Submit the request to an existing reservation
--walltime=WALLTIME Walltime requested for the node(s).
Walltime could be an integer value followed
by a unit (m, h, d, w, for minute, hour, day,
and week, respectively). E.g., 3h, 2d
--isoname=ISONAME Name of the iso image after being stored remotely.
If not specified, use the original filename
--state=COMPUTESESTATE List only computeset with the specified state.
The state could be submitted, running, completed
--link Whether to open the console url or just show the link
--concise Concise table view for cluster info
--status Cluster table view displays only those columns showing state of nodes
Arguments:
ENDPOINT Service endpoint based on the yaml config file.
By default it's either dev or production.
CLUSTERID The assigned name of a cluster, e.g. vc1
COMPUTESETID An integer identifier assigned to a computeset
COMPUTENODEID A compute node name, e.g., vm-vc1-0
If not provided, the requested action will be taken
on the frontend node of the specified cluster
COMPUTENODEIDS A set of compute node names in hostlist format,
e.g., vm-vc1-[0-3]
One single node is also acceptable: vm-vc1-0
If not provided, the requested action will be taken
on the frontend node of the specified cluster
NODESPARAM Specifying the node/nodes/computeset to act on.
In case of integer, will be intepreted as a computesetid;
in case of a hostlist format, e.g., vm-vc1-[0-3], a group
of nodes; or a single host is also acceptable,
e.g., vm-vc1-0
ISONAME Name of an iso image at remote server
ISOIDNAME Index or name of an iso image at the remote server.
The index is based on the list from 'comet iso list'.
PATHISOFILE The full path to the iso image file to be uploaded
OLDNAMES The list of current node names to be renamed, in hostlist
format. A single host is also acceptable.
NEWNAMES The list of new names to rename to, in hostlist format.
A single host is also acceptable.
"""
# back up of all the proposed commands/options
"""
comet status
comet tunnel start
comet tunnel stop
comet tunnel status
comet logon
comet logoff
comet ll [CLUSTERID] [--format=FORMAT]
comet docs
comet info [--user=USER]
[--project=PROJECT]
[--format=FORMAT]
comet cluster [CLUSTERID][--name=NAMES]
[--user=USER]
[--project=PROJECT]
[--hosts=HOSTS]
[--start=TIME_START]
[--end=TIME_END]
[--hosts=HOSTS]
[--format=FORMAT]
comet computeset [COMPUTESETID]
comet start ID
comet stop ID
comet power on CLUSTERID [NODESPARAM]
[--allocation=ALLOCATION]
[--walltime=WALLTIME]
comet power (off|reboot|reset|shutdown) CLUSTERID [NODESPARAM]
comet console CLUSTERID [COMPUTENODEID]
comet delete [all]
[--user=USER]
[--project=PROJECT]
[--name=NAMES]
[--hosts=HOSTS]
[--start=TIME_START]
[--end=TIME_END]
[--host=HOST]
comet delete --file=FILE
comet update [--name=NAMES]
[--hosts=HOSTS]
[--start=TIME_START]
[--end=TIME_END]
comet add [--user=USER]
[--project=PROJECT]
[--host=HOST]
[--description=DESCRIPTION]
[--start=TIME_START]
[--end=TIME_END]
NAME
comet add --file=FILENAME
Options:
--user=USER user name
--name=NAMES Names of the vcluster
--start=TIME_START Start time of the vcluster, in
YYYY/MM/DD HH:MM:SS format.
[default: 1901-01-01]
--end=TIME_END End time of the vcluster, in YYYY/MM/DD
HH:MM:SS format. In addition a duratio
can be specified if the + sign is the
first sig The duration will than be
added to the start time.
[default: 2100-12-31]
--project=PROJECT project id
--host=HOST host name
--description=DESCRIPTION description summary of the vcluster
--file=FILE Adding multiple vclusters from one file
--format=FORMAT Format is either table, json, yaml,
csv, rest
[default: table]
--allocation=ALLOCATION Allocation to charge when power on
node(s)
--walltime=WALLTIME Walltime requested for the node(s)
Arguments:
FILENAME the file to open in the cwd if . is
specified. If file in in cwd
you must specify it with ./FILENAME
Opens the given URL in a browser window.
"""
"""
if not arguments["tunnel"] and Comet.tunnelled and not Comet.is_tunnel():
Console.error("Please establish a tunnel first with:")
print
print (" comet tunnel start")
print
return ""
try:
if not arguments["tunnel"]:
logon = Comet.logon()
if logon is False:
Console.error("Could not logon")
return ""
except:
Console.error("Could not logon")
# pprint (arguments)
output_format = arguments["--format"] or "table"
if arguments["status"]:
Comet.state()
elif arguments["tunnel"] and arguments["start"]:
Comet.tunnel(True)
elif arguments["tunnel"] and arguments["stop"]:
Comet.tunnel(False)
elif arguments["tunnel"] and arguments["status"]:
Comet.state()
elif arguments["logon"]:
if self.context.comet_token is None:
if Comet.logon():
Console.ok("logging on")
self.context.comet_token = Comet.token
else:
Console.error("could not logon")
else:
Console.error("already logged on")
elif arguments["logoff"]:
if self.context.comet_token is None:
Console.error("not logged in")
else:
if Comet.logoff():
Console.ok("Logging off")
self.context.comet_token = None
else:
Console.error(
"some issue while logging off. Maybe comet not reachable")
elif arguments["docs"]:
Comet.docs()
elif arguments["info"]:
Console.error("not yet implemented")
elif arguments["add"]:
print ("add the cluster")
elif arguments["start"]:
cluster_id = arguments["ID"]
print("start", cluster_id)
Cluster.start(cluster_id)
elif arguments["stop"]:
cluster_id = arguments["ID"]
print("stop", cluster_id)
Cluster.stop(cluster_id)
elif arguments["ll"]:
"""
if arguments["init"]:
print ("Initializing the comet configuration file...")
config = ConfigDict("cloudmesh.yaml")
# for unit testing only.
cometConf = config["cloudmesh.comet"]
endpoints = []
# print (cometConf.keys())
if "endpoints" in cometConf.keys():
endpoints = cometConf["endpoints"].keys()
if len(endpoints) < 1:
Console.error("No service endpoints available. "
"Please check the config template",
traceflag=False)
return ""
if "username" in cometConf.keys():
default_username = cometConf['username']
# print (default_username)
if 'TBD' == default_username:
set_default_user = \
input("Set a default username (RETURN to skip): ")
if set_default_user:
config.data["cloudmesh"]["comet"]["username"] = \
set_default_user
config.save()
Console.ok("Comet default username set!")
if "active" in cometConf.keys():
active_endpoint = cometConf['active']
set_active_endpoint = \
input("Set the active service endpoint to use. "
"The availalbe endpoints are - %s [%s]: "
% ("/".join(endpoints),
active_endpoint)
)
if set_active_endpoint:
if set_active_endpoint in endpoints:
config.data["cloudmesh"]["comet"]["active"] = \
set_active_endpoint
config.save()
Console.ok("Comet active service endpoint set!")
else:
Console.error("The provided endpoint does not match "
"any available service endpoints. Try %s"
% "/".join(endpoints),
traceflag=False)
if cometConf['active'] in endpoints:
endpoint_url = cometConf["endpoints"] \
[cometConf['active']]["nucleus_base_url"]
api_version = cometConf["endpoints"] \
[cometConf['active']]["api_version"]
set_endpoint_url = \
input("Set the base url for the nucleus %s service [%s]: " \
% (cometConf['active'],
endpoint_url)
)
if set_endpoint_url:
if set_endpoint_url != endpoint_url:
config.data["cloudmesh"]["comet"]["endpoints"] \
[cometConf['active']]["nucleus_base_url"] \
= set_endpoint_url
config.save()
Console.ok("Service base url set!")
set_api_version = \
input("Set the api version for the nucleus %s service [%s]: " \
% (cometConf['active'],
api_version)
)
if set_api_version:
if set_api_version != api_version:
config.data["cloudmesh"]["comet"]["endpoints"] \
[cometConf['active']]["api_version"] \
= set_api_version
config.save()
Console.ok("Service api version set!")
print("Authenticating to the nucleus %s " \
"service and obtaining the apikey..." \
% cometConf['active'])
Comet.get_apikey(cometConf['active'])
return ''
# Comet.get_apikey()
if arguments["active"]:
config = ConfigDict("cloudmesh.yaml")
cometConf = config["cloudmesh.comet"]
endpoint = arguments["ENDPOINT"] or None
# parameter specified, intended to change
if endpoint:
if "endpoints" in cometConf.keys():
endpoints = cometConf["endpoints"].keys()
if endpoint in endpoints:
config.data["cloudmesh"] \
["comet"] \
["active"] = endpoint
config.save()
Console.ok("Comet active service endpoint set"
" to: %s" % endpoint)
else:
Console.error("The provided endpoint does not match "
"any available service endpoints. Try %s."
% "/".join(endpoints),
traceflag = False)
else:
Console.error("No available endpoint to set. "
"Check config file!",
traceflag=False)
else:
if "active" in cometConf.keys():
active_endpoint = cometConf['active']
Console.ok("Current active service endpoint is: %s"
% active_endpoint)
else:
Console.error("Cannot set active endpoint. "
"Check config file!",
traceflag = False)
try:
endpoint = None
config = ConfigDict("cloudmesh.yaml")
cometConf = config["cloudmesh.comet"]
if arguments["--endpoint"]:
endpoint = arguments["--endpoint"]
if "endpoints" in cometConf.keys():
endpoints = cometConf["endpoints"].keys()
if endpoint not in endpoints:
Console.error("The provided endpoint does not match "
"any available service endpoints. Try %s."
% "/".join(endpoints),
traceflag = False)
return ''
logon = Comet.logon(endpoint=endpoint)
if logon is False:
Console.error("Could not logon. Please try first:\n"
"cm comet init",
traceflag = False)
return ""
except:
Console.error("Could not logon",
traceflag = False)
output_format = arguments["--format"] or "table"
if arguments["ll"]:
cluster_id = arguments["CLUSTERID"] or None
print(Cluster.simple_list(cluster_id, format=output_format))
elif arguments["cluster"]:
view = "FULL"
if arguments["--concise"]:
view = "CONCISE"
if arguments["--status"]:
view = "STATE"
cluster_id = arguments["CLUSTERID"]
sortkey = arguments["--sort"]
print(Cluster.list(cluster_id,
format=output_format,
sort=sortkey,
view=view))
elif arguments["computeset"]:
computeset_id = arguments["COMPUTESETID"] or None
cluster = arguments["--cluster"] or None
state = arguments["--state"] or None
allocation = arguments["--allocation"] or None
cluster = arguments["--cluster"] or None
print (Cluster.computeset(computeset_id, cluster, state, allocation))
elif arguments["start"]:
clusterid = arguments["CLUSTERID"]
numnodes = arguments["--count"] or None
computenodeids = arguments["COMPUTENODEIDS"] or None
# check allocation information for the cluster
cluster = Cluster.list(clusterid, format='rest')
try:
allocations = cluster[0]['allocations']
except:
# print (cluster)
Console.error("No allocation available for the specified cluster."\
"Please check with the comet help team",
traceflag=False)
return ""
# checking whether the computesetids is in valid hostlist format
if computenodeids:
try:
hosts_param = hostlist.expand_hostlist(computenodeids)
except hostlist.BadHostlist:
Console.error("Invalid hosts list specified!",
traceflag=False)
return ""
elif numnodes:
try:
param = int(numnodes)
except ValueError:
Console.error("Invalid count value specified!",
traceflag=False)
return ""
if param <= 0:
Console.error("count value has to be greather than zero",
traceflag=False)
return ""
numnodes = param
else:
Console.error("You have to specify either the count of nodes, " \
"or the names of nodes in hostlist format",
traceflag=False)
return ""
walltime = arguments["--walltime"] or None
allocation = arguments["--allocation"] or None
reservation = arguments["--reservation"] or None
# validating walltime and allocation parameters
walltime = Cluster.convert_to_mins(walltime)
if not walltime:
print("No valid walltime specified. " \
"Using system default (2 days)")
if not allocation:
if len(allocations) == 1:
allocation = allocations[0]
else:
allocation = Cluster.display_get_allocation(allocations)
# issuing call to start a computeset with specified parameters
print(Cluster.computeset_start(clusterid,
computenodeids,
numnodes,
allocation,
reservation,
walltime)
)
elif arguments["terminate"]:
computesetid = arguments["COMPUTESETID"]
print(Cluster.computeset_terminate(computesetid))
elif arguments["power"]:
clusterid = arguments["CLUSTERID"] or None
fuzzyparam = arguments["NODESPARAM"] or None
# parsing nodesparam for proper action
if fuzzyparam:
try:
param = int(fuzzyparam)
subject = 'COMPUTESET'
except ValueError:
param = fuzzyparam
try:
hosts_param = hostlist.expand_hostlist(fuzzyparam)
subject = 'HOSTS'
except hostlist.BadHostlist:
Console.error("Invalid hosts list specified!",
traceflag=False)
return ""
else:
subject = 'FE'
param = None
if arguments["on"]:
action = "on"
elif arguments["off"]:
action = "off"
elif arguments["reboot"]:
action = "reboot"
elif arguments["reset"]:
action = "reset"
elif arguments["shutdown"]:
action = "shutdown"
else:
action = None
print (Cluster.power(clusterid,
subject,
param,
action)
)
elif arguments["console"]:
clusterid = arguments["CLUSTERID"]
linkonly = False
if arguments["--link"]:
linkonly = True
nodeid = None
if 'COMPUTENODEID' in arguments:
nodeid = arguments["COMPUTENODEID"]
Comet.console(clusterid, nodeid, linkonly)
elif arguments["iso"]:
if arguments["list"]:
isos = (Comet.list_iso())
idx = 0
for iso in isos:
if iso.startswith("public/"):
iso = iso.split("/")[1]
idx += 1
print ("{}: {}".format(idx, iso))
if arguments["upload"]:
isofile = arguments["PATHISOFILE"]
isofile = os.path.abspath(isofile)
if os.path.isfile(isofile):
if arguments["--isoname"]:
filename = arguments["--isoname"]
else:
filename = os.path.basename(isofile)
else:
print ("File does not exist - {}" \
.format(arguments["PATHISOFILE"]))
return ""
print(Comet.upload_iso(filename, isofile))
elif arguments["attach"]:
isoidname = arguments["ISOIDNAME"]
clusterid = arguments["CLUSTERID"]
computenodeids = arguments["COMPUTENODEIDS"] or None
print(Cluster.attach_iso(isoidname, clusterid, computenodeids))
elif arguments["detach"]:
clusterid = arguments["CLUSTERID"]
computenodeids = arguments["COMPUTENODEIDS"] or None
print(Cluster.detach_iso(clusterid, computenodeids))
elif arguments["node"]:
if arguments["info"]:
clusterid = arguments["CLUSTERID"]
nodeid = arguments["COMPUTENODEID"]
print (Cluster.node_info(clusterid, nodeid=nodeid, format=output_format))
elif arguments["rename"]:
clusterid = arguments["CLUSTERID"]
oldnames = Parameter.expand(arguments["OLDNAMES"])
newnames = Parameter.expand(arguments["NEWNAMES"])
if len(oldnames) != len(newnames):
Console.error("Length of OLDNAMES and NEWNAMES have to be the same",
traceflag=False)
return ""
else:
for newname in newnames:
if newname.strip() == "":
Console.error("Newname cannot be empty string",
traceflag=False)
return ""
cluster_data = Cluster.list(clusterid, format="rest")
if len(cluster_data) > 0:
computes = cluster_data[0]["computes"]
nodenames = [x["name"] for x in computes]
else:
Console.error("Error obtaining the cluster information",
traceflag=False)
return ""
# check if new names ar not already taken
# to be implemented
# print (oldnames)
# print (newnames)
# print (nodenames)
oldset = set(oldnames)
newset = set(newnames)
currentset = set(nodenames)
# at least one OLDNAME does not exist
if not oldset <= currentset:
Console.error("Not all OLDNAMES are valid", traceflag=False)
return ""
else:
# those unchanged nodes
keptset = currentset - oldset
# duplication between name of unchanged nodes and
# the requested NEWNAMES
if keptset & newset != set():
Console.error("Not proceeding as otherwise introducing "\
"duplicated names",
traceflag=False)
else:
for i in range(0,len(oldnames)):
oldname = oldnames[i]
newname = newnames[i]
print ("%s -> %s" % (oldname, newname))
confirm = input("Confirm batch renaming (Y/y to confirm, "\
"any other key to abort):")
if confirm.lower() == 'y':
print ("Conducting batch renaming")
for i in range(0,len(oldnames)):
oldname = oldnames[i]
newname = newnames[i]
print (Cluster.rename_node(clusterid,
oldname,
newname))
else:
print ("Action aborted!")
elif arguments["reservation"]:
if arguments["create"] or \
arguments["update"] or \
arguments["delete"]:
Console.info("Operation not supported. Please contact XSEDE helpdesk for help!")
if arguments["list"]:
if "hpcinfo" in cometConf:
hpcinfourl = cometConf["hpcinfo"]["endpoint"]
else:
Console.error("Admin feature not configured for this client", traceflag = False)
return ""
ret = requests.get("%s/reservations/%s" % (hpcinfourl,
cometConf['active'])
)
jobs = ret.json()
result = Printer.write(jobs)
print (result)
return ""