Author: justi9
Date: 2008-09-25 16:11:49 -0400 (Thu, 25 Sep 2008)
New Revision: 2546
Added:
mgmt/trunk/cumin/python/cumin/tools.py
Modified:
mgmt/trunk/bin/reschema
mgmt/trunk/cumin/bin/cumin-admin
mgmt/trunk/mint/python/mint/__init__.py
Log:
Revamps cumin-admin tool, using common command infrastructure from
parsley.
Adds list-roles, assign, and unassign commands for putting users in
roles.
Adds methods on Subject and Role for querying single items by name.
Modified: mgmt/trunk/bin/reschema
===================================================================
--- mgmt/trunk/bin/reschema 2008-09-25 19:47:16 UTC (rev 2545)
+++ mgmt/trunk/bin/reschema 2008-09-25 20:11:49 UTC (rev 2546)
@@ -1,6 +1,7 @@
#!/bin/bash -ex
-cumin-admin drop-schema --force
-cumin-admin create-schema --force
+cumin-admin drop-schema --force || :
+cumin-admin create-schema
cumin-admin add-user guest
+cumin-admin assign guest admin
cumin-load-demo-data
Modified: mgmt/trunk/cumin/bin/cumin-admin
===================================================================
--- mgmt/trunk/cumin/bin/cumin-admin 2008-09-25 19:47:16 UTC (rev 2545)
+++ mgmt/trunk/cumin/bin/cumin-admin 2008-09-25 20:11:49 UTC (rev 2546)
@@ -1,177 +1,12 @@
#!/usr/bin/python
import sys, os
-from sqlobject import connectionForURI
-from traceback import print_exc
-from getpass import getpass
-from random import sample
-from crypt import crypt
-from mint import MintDatabase, Subject, Role
-from psycopg2 import IntegrityError
-from cumin import CuminConfig
-from cumin.util import *
+from cumin.tools import CuminAdminTool
-def print_usage(config):
- config.print_usage("cumin-admin")
-
- print """Commands:
- create-schema Create the database schema
- drop-schema Drop the database schema; requires --force yes
-
- add-user NAME Add user called NAME
- remove-user NAME Remove user called NAME; requires --force yes
- list-users List existing users"""
-
-def parse_command_args():
- args = list()
- parg = ""
-
- for arg in sys.argv[1:]:
- if not arg.startswith("--") and not parg.startswith("--"):
- args.append(arg)
-
- parg = arg
-
- return args
-
-def check_permission():
- curr_uid = os.getuid()
- file_uid = os.stat(__file__).st_uid
-
- if curr_uid == 0 or curr_uid == file_uid:
- pass
- else:
- print "You don't have sufficient privileges"
- sys.exit(1)
-
-def main():
- config = CuminConfig()
- config.add_param("force", bool, False, "Don't complain and just do
it")
-
- if "-h" in sys.argv or "--help" in sys.argv:
- print_usage(config)
- sys.exit(0)
-
- # XXX I think we need to avoid using sys.argv inside of CuminConfig
-
- config.init()
-
- if config.debug:
- config.prt()
-
- args = parse_command_args()
-
- if not args:
- print "Error: no command found"
- print_usage(config)
- sys.exit(1)
-
- check_permission()
-
- command = args[0]
-
- database = MintDatabase(config.data)
- database.check()
- database.init()
-
- if command == "create-schema":
- main = os.path.join(config.home, "sql", "schema.sql")
- indexes = os.path.join(config.home, "sql", "indexes.sql")
-
- database.createSchema((main, indexes))
- elif command == "drop-schema":
- if config.force:
- database.dropSchema()
- else:
- print "Error: command create-schema requires --force yes"
- print_usage(config)
- sys.exit(1)
- elif command == "check-schema":
- database.checkSchema()
- elif command == "add-user":
- if len(args) != 2:
- print "Error: no user name given"
- print_usage(config)
- sys.exit(1)
-
- name = args[1]
-
- if Subject.selectBy(name=name).count():
- print "Error: a user called '%s' already exists" % name
- sys.exit(1)
-
- password = None
-
- while password is None:
- once = getpass("Set password: ")
- twice = getpass("Retype password: ")
-
- if once == twice:
- password = once
- else:
- print "Passwords don't match; try again"
-
- chs = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- crypted = crypt(password, "".join(sample(chs, 2)))
-
- try:
- subject = Subject(name=name, password=crypted)
-
- for role in Role.selectBy(name="user"):
- subject.addRole(role)
- break
-
- assert role
-
- subject.syncUpdate()
- except IntegrityError:
- print "Error: a user called '%s' already exists" % name
- sys.exit(1)
-
- print "User '%s' is added" % name
- elif command == "remove-user":
- if config.force:
- if len(args) != 2:
- print "Error: no user name given"
- sys.exit(1)
-
- name = args[1]
- subjects = Subject.selectBy(name=name)
-
- if subjects.count():
- for subject in subjects:
- subject.destroySelf()
- break
- else:
- print "Error: no such user '%s'" % args[1]
- sys.exit(1)
-
- print "User '%s' is removed" % name
- else:
- print "Error: command remove-user requires --force yes"
- sys.exit(1)
- elif command == "list-users":
- accounts = Subject.select(orderBy='name')
-
- print " id name"
- print "---- " + ("-" * 20)
-
- for account in accounts:
- print "%4i %s" % (account.id, account.name)
-
- count = accounts.count()
- print "(%i user%s found)" % (count, ess(count))
- else:
- print "Error: command '%s' not recognized" % command
- print_usage(config)
- sys.exit(1)
-
if __name__ == "__main__":
try:
- main()
- except SystemExit, e:
- raise e
- except:
- print_exc()
- sys.exit(1)
+ tool = CuminAdminTool("cumin-admin")
+ tool.main()
+ except KeyboardInterrupt:
+ pass
Added: mgmt/trunk/cumin/python/cumin/tools.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/tools.py (rev 0)
+++ mgmt/trunk/cumin/python/cumin/tools.py 2008-09-25 20:11:49 UTC (rev 2546)
@@ -0,0 +1,314 @@
+import sys, os
+
+from parsley.command import *
+from mint import *
+from crypt import crypt
+from getpass import getpass
+from random import sample
+from psycopg2 import IntegrityError
+
+from cumin import CuminConfig
+from util import *
+
+class BaseCuminTool(Command):
+ def __init__(self, name):
+ super(BaseCuminTool, self).__init__(None, name)
+
+ self.config = CuminConfig()
+
+ opt = CommandOption(self, "help", "h")
+ opt.description = "Print this message"
+
+ opt = CommandOption(self, "data")
+ opt.argument = "URI"
+ opt.description = "Connect to database at URI"
+
+ opt = CommandOption(self, "log-file")
+ opt.argument = "PATH"
+ opt.description = "Log to file at PATH"
+
+ opt = CommandOption(self, "log-level")
+ opt.argument = "LEVEL"
+ opt.description = "Log messages at or above LEVEL " + \
+ "('debug', 'info', 'warning',
'error')"
+
+ opt = CommandOption(self, "debug")
+ opt.description = "Enable debugging; print logging to console"
+
+ def check(self):
+ if os.getuid() not in (os.stat(__file__).st_uid, 0):
+ print "Error: You have insufficient privileges"
+ sys.exit(1)
+
+ def init(self):
+ super(BaseCuminTool, self).init()
+
+ args = sys.argv[1:]
+
+ try:
+ opts, remaining = self.parse_options(args)
+ except CommandException, e:
+ print "Error: %s" % e.message
+ e.command.print_help()
+ sys.exit(1)
+
+ self.config.init()
+ self.config.load_string_params(opts)
+
+ def run(self):
+ if self.config.debug:
+ self.config.prt()
+
+ args = sys.argv[1:]
+
+ try:
+ opts, remaining = self.parse_options(args)
+ except CommandException, e:
+ print "Error: %s" % e.message
+ e.command.print_help()
+ sys.exit(1)
+
+ try:
+ scommand = remaining[0]
+ except IndexError:
+ self.print_help()
+ sys.exit(1)
+
+ try:
+ command = self.commands_by_name[scommand]
+ except KeyError:
+ print "Error: Command '%s' is unrecognized" % scommand
+ self.print_help()
+ sys.exit(1)
+
+ try:
+ opts, args = command.parse(remaining)
+
+ if "help" in opts:
+ command.print_help()
+ return
+
+ command.run(opts, args)
+ except CommandException, e:
+ print "Error: %s" % e.message
+ e.command.print_help()
+ sys.exit(1)
+
+ def main(self):
+ self.check()
+ self.init()
+ self.run()
+
+class CuminAdminTool(BaseCuminTool):
+ def __init__(self, name):
+ super(CuminAdminTool, self).__init__(name)
+
+ self.description = "Cumin administration commands"
+ self.database = None # Set in init
+
+ command = self.CreateSchema(self, "create-schema")
+ command.description = "Create the database schema"
+
+ command = self.DropSchema(self, "drop-schema")
+ command.description = "Drop the database schema; requires --force"
+
+ opt = CommandOption(command, "force")
+ opt.description = "Don't complain and just do it"
+
+ command = self.AddUser(self, "add-user")
+ command.arguments = ("NAME",)
+ command.description = "Add a new user called NAME"
+
+ command = self.RemoveUser(self, "remove-user")
+ command.arguments = ("NAME",)
+ command.description = "Remove user called NAME; requires --force"
+
+ opt = CommandOption(command, "force")
+ opt.description = "Don't complain and just do it"
+
+ command = self.ListUsers(self, "list-users")
+ command.description = "List existing users"
+
+ command = self.ListRoles(self, "list-roles")
+ command.description = "List existing roles"
+
+ command = self.Assign(self, "assign")
+ command.description = "Add USER to ROLE"
+ command.arguments = ("USER", "ROLE")
+
+ command = self.Unassign(self, "unassign")
+ command.description = "Remove USER from ROLE"
+ command.arguments = ("USER", "ROLE")
+
+ def init(self):
+ super(CuminAdminTool, self).init()
+
+ self.database = MintDatabase(self.config.data)
+ self.database.check()
+ self.database.init()
+
+ class CreateSchema(Command):
+ def run(self, opts, args):
+ main = os.path.join(self.parent.config.home, "sql",
"schema.sql")
+ indexes = os.path.join(self.parent.config.home, "sql",
"indexes.sql")
+
+ self.parent.database.createSchema((main, indexes))
+
+ class DropSchema(Command):
+ def run(self, opts, args):
+ if "force" in opts:
+ self.parent.database.dropSchema()
+ print "The schema is dropped"
+ else:
+ raise CommandException \
+ (self, "Command create-schema requires --force")
+
+ class CheckSchema(Command):
+ def run(self, opts, args):
+ self.parent.database.checkSchema()
+
+ class AddUser(Command):
+ def run(self, opts, args):
+ try:
+ name = args[1]
+ except IndexError:
+ raise CommandException(self, "NAME is required")
+
+ if Subject.selectBy(name=name).count():
+ print "Error: a user called '%s' already exists" %
name
+ sys.exit(1)
+
+ password = None
+
+ while password is None:
+ once = getpass("Set password: ")
+ twice = getpass("Retype password: ")
+
+ if once == twice:
+ password = once
+ else:
+ print "Passwords don't match; try again"
+
+ chs =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ crypted = crypt(password, "".join(sample(chs, 2)))
+
+ try:
+ subject = Subject(name=name, password=crypted)
+
+ for role in Role.selectBy(name="user"):
+ subject.addRole(role)
+ break
+
+ assert role
+
+ subject.syncUpdate()
+ except IntegrityError:
+ print "Error: a user called '%s' already exists" %
name
+ sys.exit(1)
+
+ print "User '%s' is added" % name
+
+ class RemoveUser(Command):
+ def run(self, opts, args):
+ if "force" in opts:
+ if len(args) != 2:
+ print "Error: no user name given"
+ sys.exit(1)
+
+ name = args[1]
+ subjects = Subject.selectBy(name=name)
+
+ if subjects.count():
+ for subject in subjects:
+ subject.destroySelf()
+ break
+ else:
+ raise CommandException(self, "User '%s' is unknown"
% name)
+
+ print "User '%s' is removed" % name
+ else:
+ raise CommandException \
+ (self, "Command remove-user requires --force yes")
+
+ class ListUsers(Command):
+ def run(self, opts, args):
+ subjects = Subject.select(orderBy='name')
+
+ print " ID Name Roles"
+ print "---- -------------------- --------------------"
+
+ for subject in subjects:
+ roles = ", ".join([x.name for x in list(subject.roles)])
+
+ print "%4i %-20s %-20s" % (subject.id, subject.name, roles)
+
+ count = subjects.count()
+ print "(%i user%s found)" % (count, ess(count))
+
+ class ListRoles(Command):
+ def run(self, opts, args):
+ roles = Role.select(orderBy='name')
+
+ print " ID Name"
+ print "---- --------------------"
+
+ for role in roles:
+ print "%4i %s" % (role.id, role.name)
+
+ count = roles.count()
+ print "(%i role%s found)" % (count, ess(count))
+
+ class Assign(Command):
+ def run(self, opts, args):
+ if len(args) != 3:
+ raise CommandException(self, "USER and ROLE are required")
+
+ subject = Subject.getByName(args[1])
+
+ if not subject:
+ raise CommandException \
+ (self, "User '%s' is unknown" % subject.name)
+
+ role = Role.getByName(args[2])
+
+ if not role:
+ raise CommandException \
+ (self, "Role '%s' is unknown" % role.name)
+
+ try:
+ subject.addRole(role)
+ subject.syncUpdate()
+
+ print "User '%s' is added to role '%s'" % \
+ (subject.name, role.name)
+ except IntegrityError:
+ msg = "User '%s' is already assigned to role
'%s'" % \
+ (subject.name, role.name)
+ raise CommandException(self, msg)
+
+ class Unassign(Command):
+ def run(self, opts, args):
+ if len(args) != 3:
+ raise CommandException(self, "USER and ROLE are required")
+
+ subject = Subject.getByName(args[1])
+
+ if not subject:
+ raise CommandException \
+ (self, "User '%s' is unknown" % subject.name)
+
+ role = Role.getByName(args[2])
+
+ if not role:
+ raise CommandException \
+ (self, "Role '%s' is unknown" % role.name)
+
+ subject.removeRole(role)
+ subject.syncUpdate()
+
+ print "User '%s' is removed from role '%s'" % \
+ (subject.name, role.name)
+
+ class ChangePassword(Command):
+ def run(self, opts, args):
+ pass
Modified: mgmt/trunk/mint/python/mint/__init__.py
===================================================================
--- mgmt/trunk/mint/python/mint/__init__.py 2008-09-25 19:47:16 UTC (rev 2545)
+++ mgmt/trunk/mint/python/mint/__init__.py 2008-09-25 20:11:49 UTC (rev 2546)
@@ -129,6 +129,14 @@
roles = SQLRelatedJoin("Role",
intermediateTable="subject_role_mapping",
createRelatedTable=False)
+ def getByName(cls, name):
+ try:
+ return Subject.selectBy(name=name)[0]
+ except IndexError:
+ pass
+
+ getByName = classmethod(getByName)
+
class Role(SQLObject):
class sqlmeta:
lazyUpdate = True
@@ -138,6 +146,14 @@
intermediateTable="subject_role_mapping",
createRelatedTable=False)
+ def getByName(cls, name):
+ try:
+ return Role.selectBy(name=name)[0]
+ except IndexError:
+ pass
+
+ getByName = classmethod(getByName)
+
class SubjectRoleMapping(SQLObject):
class sqlmeta:
lazyUpdate = True