You are viewing a plain text version of this content. The canonical link for it is here.
Posted to tashi-commits@incubator.apache.org by st...@apache.org on 2012/07/27 04:17:03 UTC
svn commit: r1366271 - /incubator/tashi/trunk/src/tashi/client/tashi-admin.py
Author: stroucki
Date: Fri Jul 27 04:17:03 2012
New Revision: 1366271
URL: http://svn.apache.org/viewvc?rev=1366271&view=rev
Log:
start new tashi-admin client program
Added:
incubator/tashi/trunk/src/tashi/client/tashi-admin.py (with props)
Added: incubator/tashi/trunk/src/tashi/client/tashi-admin.py
URL: http://svn.apache.org/viewvc/incubator/tashi/trunk/src/tashi/client/tashi-admin.py?rev=1366271&view=auto
==============================================================================
--- incubator/tashi/trunk/src/tashi/client/tashi-admin.py (added)
+++ incubator/tashi/trunk/src/tashi/client/tashi-admin.py Fri Jul 27 04:17:03 2012
@@ -0,0 +1,648 @@
+#!/usr/bin/python
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import os.path
+import random
+import sys
+import types
+from tashi.rpycservices.rpyctypes import NetworkConfiguration,\
+ DiskConfiguration, HostState, Instance, Host, TashiException
+from tashi.utils.config import Config
+from tashi import vmStates, hostStates, boolean, stringPartition, createClient
+
+users = {}
+networks = {}
+
+def fetchUsers():
+ if (users == {}):
+ _users = client.getUsers()
+ for user in _users:
+ users[user.id] = user
+
+def fetchNetworks():
+ if (networks == {}):
+ _networks = client.getNetworks()
+ for network in _networks:
+ networks[network.id] = network
+
+def getUser():
+ fetchUsers()
+ if client.username != None:
+ userStr = client.username
+ else:
+ userStr = os.getenv("USER", "unknown")
+ for user in users:
+ if (users[user].name == userStr):
+ return users[user].id
+ raise TashiException({'msg':"Unknown user %s" % (userStr)})
+
+def checkHid(host):
+ userId = getUser()
+ hosts = client.getHosts()
+ hostId = None
+ try:
+ hostId = int(host)
+ except:
+ for h in hosts:
+ if (h.name == host):
+ hostId = h.id
+ if (hostId is None):
+ raise TashiException({'msg':"Unknown host %s" % (str(host))})
+
+ # XXXstroucki permissions for host related stuff?
+ return hostId
+
+def checkIid(instance):
+ userId = getUser()
+ instances = client.getInstances()
+ instanceId = None
+ try:
+ instanceId = int(instance)
+ except:
+ for i in instances:
+ if (i.name == instance):
+ instanceId = i.id
+ if (instanceId is None):
+ raise TashiException({'msg':"Unknown instance %s" % (str(instance))})
+ for instance in instances:
+ if (instance.id == instanceId):
+ # XXXstroucki uid 0 to have superuser access
+ # how about admin groups?
+ if (instance.userId != userId and instance.userId != None and userId != 0):
+ raise TashiException({'msg':"You don't have permissions on VM %s" % instance.name})
+ return instanceId
+
+def requiredArg(name):
+ raise ValueError("Missing required argument %s" % (name))
+
+def randomMac():
+ return ("52:54:00:%2.2x:%2.2x:%2.2x" % (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
+
+def getDefaultNetwork():
+ fetchNetworks()
+ networkId = 0
+ for network in networks:
+ if (getattr(networks[network], "default", False) is True):
+ networkId = network
+ break
+
+ # Naming the network "default" is deprecated, and
+ # this functionality will be removed soon
+ if (networks[network].name == "default"):
+ networkId = network
+ break
+ return networkId
+
+def randomNetwork():
+ return [NetworkConfiguration(d={'mac':randomMac(), 'network':getDefaultNetwork()})]
+
+def parseDisks(arg):
+ try:
+ strDisks = arg.split(",")
+ disks = []
+ for strDisk in strDisks:
+ strDisk = strDisk.strip()
+ (l, __s, r) = stringPartition(strDisk, ":")
+ if (r == ""):
+ r = "False"
+ r = boolean(r)
+ disk = DiskConfiguration(d={'uri':l, 'persistent':r})
+ disks.append(disk)
+ return disks
+ except:
+ raise ValueError("Incorrect format for disks argument")
+
+def parseNics(arg):
+ try:
+ strNics = arg.split(",")
+ nics = []
+ for strNic in strNics:
+ strNic = strNic.strip()
+ (l, __s, r) = stringPartition(strNic, ":")
+ n = l
+ if (n == ''):
+ n = getDefaultNetwork()
+ n = int(n)
+ (l, __s, r) = stringPartition(r, ":")
+ ip = l
+ if (ip == ''):
+ ip = None
+ m = r
+ if (m == ''):
+ m = randomMac()
+ nic = NetworkConfiguration(d={'mac':m, 'network':n, 'ip':ip})
+ nics.append(nic)
+ return nics
+ except:
+ raise ValueError("Incorrect format for nics argument")
+
+def parseHints(arg):
+ try:
+ strHints = arg.split(",")
+ hints = {}
+ for strHint in strHints:
+ strHint = strHint.strip()
+ (l, __s, r) = stringPartition(strHint, "=")
+ hints[l] = r
+ return hints
+ except:
+ raise ValueError("Incorrect format for hints argument")
+
+def getVmLayout():
+ _hosts = client.getHosts()
+ _instances = client.getInstances()
+ hosts = {}
+ for h in _hosts:
+ h.instances = []
+ h.instanceIds = []
+ h.usedMemory = 0
+ h.usedCores = 0
+ hosts[h.id] = h
+ for i in _instances:
+ if (i.hostId in hosts):
+ hosts[i.hostId].instanceIds.append(i.id)
+ hosts[i.hostId].instances.append(i.name)
+ hosts[i.hostId].usedMemory += i.memory
+ hosts[i.hostId].usedCores += i.cores
+ return hosts.values()
+
+def getSlots(cores, memory):
+ hosts = getVmLayout()
+ count = 0
+
+ if cores < 1:
+ print "Argument to cores must be 1 or greater."
+ return
+
+ if memory <= 0:
+ print "Argument to memory must be greater than 0."
+ return
+
+ for h in hosts:
+ if h.up is False or h.state != HostState.Normal:
+ continue
+ countbycores = int((h.cores - h.usedCores) / cores)
+ countbymemory = int((h.memory - h.usedMemory) / memory)
+ count += max(0, min(countbycores, countbymemory))
+
+ print "%d" % (count), "available slots for",
+ print ("instances", "instance")[count == 1],
+ print "with %d" % (cores),
+ print ("cores", "core")[cores == 1],
+ print "and %d MB memory." % (memory)
+
+def createMany(instance, count):
+ # will create instances from 0 to count-1
+ l = len(str(count - 1))
+ basename = instance.name
+ instances = []
+ for i in range(0, count):
+ for nic in instance.nics:
+ nic.mac = randomMac()
+ instance.name = basename + (("-%" + str(l) + "." + str(l) + "d") % (i))
+ instances.append(client.createVm(instance))
+ return instances
+
+def shutdownMany(basename):
+ return __shutdownOrDestroyMany("shutdown", basename)
+
+def destroyMany(basename):
+ return __shutdownOrDestroyMany("destroy", basename)
+
+def __shutdownOrDestroyMany(method, basename):
+ instances = client.getInstances()
+ count = 0
+ for i in instances:
+ if (i.name.startswith(basename + "-") and i.name[len(basename)+1].isdigit()):
+ # checking permissions here
+ checkIid(i.name)
+
+ if method == "shutdown":
+ client.shutdownVm(i.id)
+
+ elif method == "destroy":
+ client.destroyVm(i.id)
+
+ else:
+ raise ValueError("Unknown method")
+
+ count = count + 1
+ if (count == 0):
+ raise TashiException({'msg':"%s is an unused basename" % basename})
+ return None
+
+def getMyInstances():
+ userId = getUser()
+ _instances = client.getInstances()
+ instances = []
+ for i in _instances:
+ if (i.userId == userId):
+ instances.append(i)
+ return instances
+
+# Used to define default views on functions and to provide extra functionality (getVmLayout)
+extraViews = {
+'getSlots': (getSlots, None),
+'getImages': (None, ['id', 'imageName', 'imageSize']),
+'copyImage': (None, None),
+'createVm': (None, ['id', 'hostId', 'name', 'user', 'state', 'disk', 'memory', 'cores']),
+'createMany': (createMany, ['id', 'hostId', 'name', 'user', 'state', 'disk', 'memory', 'cores']),
+'shutdownMany': (shutdownMany, None),
+'destroyMany': (destroyMany, None),
+'getVmLayout': (getVmLayout, ['id', 'name', 'state', 'instances', 'usedMemory', 'memory', 'usedCores', 'cores']),
+'getInstances': (None, ['id', 'hostId', 'name', 'user', 'state', 'disk', 'memory', 'cores']),
+'getMyInstances': (getMyInstances, ['id', 'hostId', 'name', 'user', 'state', 'disk', 'memory', 'cores'])
+}
+
+('addHost', 'Adds a new host to Tashi'),
+('delHost', 'Removes a host from Tashi'),
+('addUser', 'Adds a user to Tashi'),
+('delUser', 'Removes a user from Tashi'),
+('addNet', 'Adds a network to Tashi'),
+('delNet', 'Removes a network from Tashi'),
+('setHostState', 'Set the state of a host, eg. Normal or Drained'),
+('setHostNotes', 'Annotate a host record'),
+# Used to specify what args are excepted for a function, what to use to convert the string to a value, what to use as a default value if it's missing, and whether the argument was required or not
+argLists = {
+'addHost': [('name', str, lambda: requiredArg('name'), True)],
+'delHost': [('name', str, lambda: requiredArg('name'), True)],
+'addUser': [('name', str, lambda: requiredArg('name'), True)],
+'delUser': [('name', str, lambda: requiredArg('name'), True)],
+'addNet': [('name', str, lambda: requiredArg('host'), True)],
+'delNet': [('host', str, lambda: requiredArg('host'), True)],
+'setHostState': [('host', checkHid, lambda: requiredArg('host'), True), ('state', str, lambda: requiredArg('state'), True)],
+'setHostNotes': [('host', checkHid, lambda: requiredArg('host'), True), ('notes', str, lambda: requiredArg('notes'), True)],
+}
+
+# Used to convert the dictionary built from the arguments into an object that can be used by rpyc
+convertArgs = {
+'createVm': '[Instance(d={"userId":userId,"name":name,"cores":cores,"memory":memory,"disks":disks,"nics":nics,"hints":hints})]',
+'createMany': '[Instance(d={"userId":userId,"name":basename,"cores":cores,"memory":memory,"disks":disks,"nics":nics,"hints":hints}), count]',
+'shutdownVm': '[instance]',
+'destroyVm': '[instance]',
+'shutdownMany': '[basename]',
+'destroyMany': '[basename]',
+'suspendVm': '[instance]',
+'resumeVm': '[instance]',
+'migrateVm': '[instance, dst]',
+'pauseVm': '[instance]',
+'unpauseVm': '[instance]',
+'vmmSpecificCall': '[instance, arg]',
+'unregisterHost' : '[hostId]',
+'getSlots' : '[cores, memory]',
+'copyImage' : '[src, dst]',
+'setHostState' : '[host, state]',
+}
+
+# Descriptions
+description = (
+('addHost', 'Adds a new host to Tashi'),
+('delHost', 'Removes a host from Tashi'),
+('addUser', 'Adds a user to Tashi'),
+('delUser', 'Removes a user from Tashi'),
+('addNet', 'Adds a network to Tashi'),
+('delNet', 'Removes a network from Tashi'),
+('setHostState', 'Set the state of a host, eg. Normal or Drained'),
+('setHostNotes', 'Annotate a host record'),
+)
+
+# Example use strings
+examples = (
+('addHost', ('--name <host name>',)),
+('delHost', ('--name <host name>',)),
+('addUser', ('--name <user name>',)),
+('delUser', ('--name <user name>',)),
+('addNet', ('--name <network name> --id <VLAN ID>',)),
+('delNet', ('--name <network name>','--id <VLAN ID>',)),
+('setHostState', ('--host <host name> --state <new state>',)),
+('setHostNotes', ('--host <host name> --text <text string>',)),
+)
+
+show_hide = []
+
+def usage(func = None):
+ """Print program usage"""
+ if (func == None or func not in argLists):
+ if (func != None):
+ print "Unknown function %s" % (func)
+ print
+ functions = argLists
+ print "%s is the administrative tool for Tashi" % (os.path.basename(sys.argv[0]))
+ print "Tashi, a system for cloud-computing on BigData"
+ print "Visit http://incubator.apache.org/tashi/ for more information."
+ print
+ else:
+ functions = {func: argLists[func]}
+ print "Usage:"
+ for f in functions:
+ args = argLists[f]
+ line = "\t" + f
+ for arg in args:
+ if (arg[3]):
+ line += " --%s <value>" % (arg[0])
+ else:
+ line += " [--%s <value>]" % (arg[0])
+ print line
+ if ("--help" in sys.argv and f in description):
+ print
+ print "\t\t" + description[f]
+ print
+ if ("--examples" in sys.argv):
+ if ("--help" not in sys.argv or f not in description):
+ print
+ for example in examples.get(f, []):
+ print "\t\t" + f + " " + example
+ print
+ if ("--help" not in sys.argv and "--examples" not in sys.argv):
+ print
+ print "Additionally, all functions accept --show-<name> and --hide-<name>, which show and hide columns during table generation"
+ if ("--examples" not in sys.argv):
+ print "Use \"--examples\" to see examples"
+ sys.exit(-1)
+
+def transformState(obj):
+ if (type(obj) == Instance):
+ fetchUsers()
+ try:
+ obj.state = vmStates[obj.state]
+ except:
+ obj.state = 'Unknown'
+ if (obj.userId in users):
+ obj.user = users[obj.userId].name
+ else:
+ obj.user = None
+ obj.disk = obj.disks[0].uri
+ if (obj.disks[0].persistent):
+ obj.disk += ":True"
+ elif (type(obj) == Host):
+ try:
+ obj.state = hostStates[obj.state]
+ except:
+ obj.state = 'Unknown'
+
+def genKeys(_list):
+ keys = {}
+ for row in _list:
+ for item in row.__dict__.keys():
+ keys[item] = item
+ if ('id' in keys):
+ del keys['id']
+ keys = ['id'] + keys.values()
+ else:
+ keys = keys.values()
+ return keys
+
+def makeTable(_list, keys=None):
+ (consoleWidth, __consoleHeight) = (9999, 9999)
+ try:
+# XXXpipe: get number of rows and column on current window
+ stdout = os.popen("stty size")
+ __r = stdout.read()
+ stdout.close()
+ except:
+ pass
+ for obj in _list:
+ transformState(obj)
+ if (keys == None):
+ keys = genKeys(_list)
+ for (show, k) in show_hide:
+ if (show):
+ if (k != "all"):
+ keys.append(k)
+ else:
+ keys = genKeys(_list)
+ else:
+ if (k in keys):
+ keys.remove(k)
+ if (k == "all"):
+ keys = []
+ maxWidth = {}
+ for k in keys:
+ maxWidth[k] = len(k)
+ for row in _list:
+ for k in keys:
+ if (k in row.__dict__):
+ maxWidth[k] = max(maxWidth[k], len(str(row.__dict__[k])))
+ if (keys == []):
+ return
+ totalWidth = reduce(lambda x, y: x + y + 1, maxWidth.values(), 0)
+ while (totalWidth > consoleWidth):
+ widths = maxWidth.items()
+ widths.sort(cmp=lambda x, y: cmp(x[1], y[1]))
+ widths.reverse()
+ maxWidth[widths[0][0]] = widths[0][1]-1
+ totalWidth = reduce(lambda x, y: x + y + 1, maxWidth.values(), 0)
+ line = ""
+ for k in keys:
+ if (len(str(k)) > maxWidth[k]):
+ line += (" %-" + str(maxWidth[k]-3) + "." + str(maxWidth[k]-3) + "s...") % (k)
+ else:
+ line += (" %-" + str(maxWidth[k]) + "." + str(maxWidth[k]) + "s") % (k)
+ print line
+ line = ""
+ for k in keys:
+ line += ("-" * (maxWidth[k]+1))
+ print line
+ def sortFunction(a, b):
+ av = a.__dict__[keys[0]]
+ bv = b.__dict__[keys[0]]
+ if (av < bv):
+ return -1
+ elif (av > bv):
+ return 1
+ else:
+ return 0
+ _list.sort(cmp=sortFunction)
+ for row in _list:
+ line = ""
+ for k in keys:
+ row.__dict__[k] = row.__dict__.get(k, "")
+ if (len(str(row.__dict__[k])) > maxWidth[k]):
+ line += (" %-" + str(maxWidth[k]-3) + "." + str(maxWidth[k]-3) + "s...") % (str(row.__dict__[k]))
+ else:
+ line += (" %-" + str(maxWidth[k]) + "." + str(maxWidth[k]) + "s") % (str(row.__dict__[k]))
+ print line
+
+def simpleType(obj):
+ """Determines whether an object is a simple type -- used as a helper function to pprint"""
+ if (type(obj) is not types.ListType):
+ if (not getattr(obj, "__dict__", None)):
+ return True
+ return False
+
+def pprint(obj, depth = 0, key = None):
+ """My own version of pprint that prints out a dict in a readable, but slightly more compact format"""
+ valueManip = lambda x: x
+ if (key):
+ keyString = key + ": "
+ if (key == "state"):
+ valueManip = lambda x: vmStates[x]
+ else:
+ keyString = ""
+ if (type(obj) is types.ListType):
+ if (reduce(lambda x, y: x and simpleType(y), obj, True)):
+ print (" " * (depth * INDENT)) + keyString + str(obj)
+ else:
+ print (" " * (depth * INDENT)) + keyString + "["
+ for o in obj:
+ pprint(o, depth + 1)
+ print (" " * (depth * INDENT)) + "]"
+ elif (getattr(obj, "__dict__", None)):
+ if (reduce(lambda x, y: x and simpleType(y), obj.__dict__.itervalues(), True)):
+ print (" " * (depth * INDENT)) + keyString + str(obj)
+ else:
+ print (" " * (depth * INDENT)) + keyString + "{"
+ for (k, v) in obj.__dict__.iteritems():
+ pprint(v, depth + 1, k)
+ print (" " * (depth * INDENT)) + "}"
+ else:
+ print (" " * (depth * INDENT)) + keyString + str(valueManip(obj))
+
+def matchFunction(func):
+ if (func == "--help" or func == "--examples"):
+ usage()
+ lowerFunc = func.lower()
+ lowerFuncsList = map(lambda x: (x.lower(), x), argLists.keys())
+ lowerFuncs = {}
+ for (l, f) in lowerFuncsList:
+ lowerFuncs[l] = f
+ if (lowerFunc in lowerFuncs):
+ return lowerFuncs[lowerFunc]
+ usage(func)
+
+def main():
+ """Main function for the client program"""
+ global INDENT, exitCode, client
+ exitCode = 0
+ exception = None
+ INDENT = (os.getenv("INDENT", 4))
+ if (len(sys.argv) < 2):
+ usage()
+ function = matchFunction(sys.argv[1])
+ config = Config(["Client"])
+
+ # build a structure of possible arguments
+ possibleArgs = {}
+ argList = argLists[function]
+ for i in range(0, len(argList)):
+ possibleArgs[argList[i][0]]=argList[i]
+
+ args = sys.argv[2:]
+
+ vals = {}
+
+ try:
+ # create client handle
+ client = createClient(config)
+
+ # set defaults
+ for parg in possibleArgs.values():
+ (parg, conv, default, required) = parg
+ if (required is False):
+ vals[parg] = default()
+
+ while (len(args) > 0):
+ arg = args.pop(0)
+
+ if (arg == "--help" or arg == "--examples"):
+ usage(function)
+ # this exits
+
+ if (arg.startswith("--hide-")):
+ show_hide.append((False, arg[7:]))
+ continue
+
+ if (arg.startswith("--show-")):
+ show_hide.append((True, arg[7:]))
+ continue
+
+ if (arg.startswith("--")):
+ if (arg[2:] in possibleArgs):
+ (parg, conv, default, required) = possibleArgs[arg[2:]]
+ try:
+ val = None
+ lookahead = args[0]
+ if not lookahead.startswith("--"):
+ val = args.pop(0)
+ except:
+ pass
+
+ val = conv(val)
+ if (val == None):
+ val = default()
+
+ vals[parg] = val
+ continue
+ # somewhat lame, but i don't want to rewrite the fn at this time
+ exception = ValueError("Unknown argument %s" % (arg))
+
+ f = None
+ try:
+ f = extraViews[function][0]
+ except:
+ pass
+
+ if (f is None):
+ f = getattr(client, function, None)
+
+ try:
+ if exception is not None:
+ raise exception
+
+ if (function in convertArgs):
+ fargs = eval(convertArgs[function], globals(), vals)
+ else:
+ fargs = []
+
+ res = f(*fargs)
+
+ except TashiException, e:
+ print "Failed in calling %s: %s" % (function, e.msg)
+ sys.exit(-1)
+
+ except Exception, e:
+ print "Failed in calling %s: %s" % (function, e)
+ print "Please run tashi-admin --examples for syntax information"
+ sys.exit(-1)
+
+ if (res != None):
+ keys = extraViews.get(function, (None, None))[1]
+ try:
+ if (type(res) == types.ListType):
+ makeTable(res, keys)
+ elif (type(res) == types.StringType):
+ print res
+ else:
+ makeTable([res], keys)
+
+ except IOError:
+ pass
+ except Exception, e:
+ print e
+ except TashiException, e:
+ print "Tashi could not complete your request:"
+ print e.msg
+ exitCode = e.errno
+ except Exception, e:
+ print e
+ print "Please run tashi-admin --examples for syntax information"
+ sys.exit(exitCode)
+
+if __name__ == "__main__":
+ main()
Propchange: incubator/tashi/trunk/src/tashi/client/tashi-admin.py
------------------------------------------------------------------------------
svn:executable = *