#!/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. # -------------------------------------------------------------------------- # these three variables can be specified here an omitted at the command line # if specified in both places, the command line options have priority #vcluser = '' # log in to the VCL site with this user, must be in username@affiliation form #vclpass = '' # password for vcluser #vclurl = 'https://vcl.ncsu.edu/scheduling/index.php?mode=xmlrpccall' # URL of VCL site # -------------------------------------------------------------------------- import sys, os, stat, re import xmlrpc.client, http.client import urllib from optparse import OptionParser class VCLTransport(xmlrpc.client.Transport): __userid = '' __passwd = '' def addAuth(self, userid, passwd): self.__userid = userid self.__passwd = passwd def addAuthHeaders(self): self._extra_headers.append(('X-APIVERSION', '2')) self._extra_headers.append(('X-User', self.__userid)) self._extra_headers.append(('X-Pass', self.__passwd)) class VCLSafeTransport(VCLTransport): """Handles an HTTPS transaction to an XML-RPC server.""" def __init__(self, use_datetime=False, use_builtin_types=False, *, context=None): super().__init__(use_datetime=use_datetime, use_builtin_types=use_builtin_types) self.context = context def make_connection(self, host): if self._connection and host == self._connection[0]: return self._connection[1] if not hasattr(http.client, "HTTPSConnection"): raise NotImplementedError( "your version of http.client doesn't support HTTPS") # create a HTTPS connection object from a host descriptor # host may be a string, or a (host, x509-dict) tuple chost, self._extra_headers, x509 = self.get_host_info(host) self._connection = host, http.client.HTTPSConnection(chost, None, context=self.context, **(x509 or {})) self.addAuthHeaders() return self._connection[1] class VCLServerProxy(xmlrpc.client.ServerProxy): __userid = '' __passwd = '' def __init__(self, uri, userid, passwd, transport=None, encoding=None, verbose=False, allow_none=False, use_datetime=False, use_builtin_types=False, *, context=None): self.__userid = userid self.__passwd = passwd # establish a "logical" server connection # get the url type, uri = urllib.parse.splittype(uri) if type not in ("http", "https"): raise IOError("unsupported XML-RPC protocol") self.__host, self.__handler = urllib.parse.splithost(uri) if not self.__handler: self.__handler = "/RPC2" if transport is None: if type == "https": handler = VCLSafeTransport extra_kwargs = {"context": context} else: handler = VCLTransport extra_kwargs = {} transport = handler(use_datetime=use_datetime, use_builtin_types=use_builtin_types, **extra_kwargs) transport.addAuth(self.__userid, self.__passwd) self.__transport = transport self.__encoding = encoding or 'utf-8' self.__verbose = verbose self.__allow_none = allow_none def __request(self, methodname, params): # call a method on the remote server request = xmlrpc.client.dumps(params, methodname, encoding=self.__encoding, allow_none=self.__allow_none).encode(self.__encoding) response = self.__transport.request( self.__host, self.__handler, request, verbose=self.__verbose ) if len(response) == 1: response = response[0] return response def __getattr__(self, name): # magic method dispatcher return xmlrpc.client._Method(self.__request, name) def addUserGroup(options): caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCaddUserGroup(options.name, options.affiliation, options.owner, options.managingGroup, options.initialmax, options.totalmax, options.maxextend, options.custom) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) elif rc['status'] == 'success': print("SUCCESS: User group sucessfully created") else: print("ERROR: unknown problem while creating new user group") def getUserGroupAttributes(options): caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCgetUserGroupAttributes(options.name, options.affiliation) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) elif rc['status'] == 'success': print("SUCCESS: Attributes retreived") print('owner: %s\nmanagingGroup: %s\ninitialMaxTime: %s\ntotalMaxTime: %s\nmaxExtendTime: %s' % (rc['owner'], rc['managingGroup'], rc['initialMaxTime'], rc['totalMaxTime'], rc['maxExtendTime'])) else: print("ERROR: unknown problem while getting user group attributes") def deleteUserGroup(options): caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCdeleteUserGroup(options.name, options.affiliation) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) elif rc['status'] == 'success': print("SUCCESS: User group sucessfully deleted") else: print("ERROR: unknown problem while deleting user group") def editUserGroup(options): caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCeditUserGroup(options.name, options.affiliation, options.newname, options.newaffiliation, options.owner, options.managingGroup, options.initialmax, options.totalmax, options.maxextend) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) elif rc['status'] == 'success': print("SUCCESS: User group sucessfully updated") else: print("ERROR: unknown problem while updating user group") def getUserGroupMembers(options): caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCgetUserGroupMembers(options.name, options.affiliation) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) elif rc['status'] == 'success': print("SUCCESS: Membership retrieved") for item in rc['members']: print(item) else: print("ERROR: unknown problem while getting user group members") def addUsersToGroup(options): newusers = [] if options.filename: fp = open(options.filename, 'r') for line in fp: newusers += [line.rstrip()] fp.close() if options.userlist: newusers += options.userlist.split(',') if len(newusers) == 0: print("WARNING: no users specified to add to group, not doing anything") sys.exit(4) caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCaddUsersToGroup(options.name, options.affiliation, newusers) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) elif rc['status'] == 'success': print("SUCCESS: Users sucessfully added to group") else: print("ERROR: unknown problem while adding users to group") def removeUsersFromGroup(options): delusers = [] if options.filename: fp = open(options.filename, 'r') for line in fp: delusers += [line.rstrip()] fp.close() if options.userlist: delusers += options.userlist.split(',') if len(delusers) == 0: print("WARNING: no users specified to remove from group, not doing anything") sys.exit(4) caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCremoveUsersFromGroup(options.name, options.affiliation, delusers) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) elif rc['status'] == 'success': print("SUCCESS: Users sucessfully removed from group") else: print("ERROR: unknown problem while removing users from group") def emptyGroupMembership(options): caller = VCLServerProxy(options.vclurl, options.vcluser, options.vclpass) rc = caller.XMLRPCgetUserGroupMembers(options.name, options.affiliation) if(rc['status'] == 'error'): print('ERROR: There was an error with the API call: ' + rc['errormsg']) sys.exit(5) else: options.userlist = ','.join(rc['members']) options.filename = None removeUsersFromGroup(options) def printHelp(command='all'): if command == 'all': print("Usage: \n") print(" %s [-u vcluser] [-p 'vclpass'] [-r vclurl] \n" % (sys.argv[0])) print(" These first three options can be omitted by defining them at the top of %s" % (sys.argv[0])) print(" -u vcluser - log in to VCL site with this user, must be in username@affiliation form") print(" -p 'vclpass' - password used when logging in to VCL site, use quotes if it contains spaces") print(" -r vclurl - URL of VCL site\n") print("Commands:\n") if command == 'all' or command == 'addUserGroup': print(" addUserGroup - creates a new user group") print(" parameters:") print(" -n name - name of new user group") print(" -a affiliation - affiliation of new user group") print(" -o owner - user that will be the owner of the group in username@affiliation form") print(" -m ManagingGroup - name of user group that can manage membership of the new group") print(" -i InitialMaxTime - (minutes) max initial time users in this group can select for length of reservations") print(" -t TotalMaxTime - (minutes) total length users in the group can have for a reservation (including all extensions)") print(" -x MaxExtendTime - (minutes) max length of time users can request as an extension to a reservation at a time") print(" -c custom (optional, default=1) - whether or not the group should be created as a custom group; if not custom ") print(" the group membership will be managed via the user's authentication protocol\n") if command == 'all' or command == 'getUserGroupAttributes': print(" getUserGroupAttributes - gets information about a user group") print(" parameters:") print(" -n name - name of an existing user group") print(" -a affiliation - affiliation of user group\n") if command == 'all' or command == 'deleteUserGroup': print(" deleteUserGroup - deletes a user group") print(" parameters:") print(" -n name - name of an existing user group") print(" -a affiliation - affiliation of user group\n") if command == 'all' or command == 'editUserGroup': print(" editUserGroup - modifies attributes of a user group") print(" parameters:") print(" -n name - name of an existing user group") print(" -a affiliation - affiliation of user group") print(" -N NewName - (optional) new name for the user group") print(" -A NewAffiliation - (optional) new affiliation for the user group") print(" -O NewOwner - (optional) new owner for the user group in username@affiliation form") print(" -M NewManagingGroup - (optional) new user group that can manage membership of the user group in group@affiliation form") print(" -I NewInitialMaxTime - (optional) new max initial time users in the group can select for length of reservations") print(" -T NewTotalMaxTime - (optional) new total length users in the group can have for a reservation (including all extensions)") print(" -X NewMaxExtendTime - (optional) new max length of time users can request as an extension to a reservation at a time\n") if command == 'all' or command == 'getUserGroupMembers': print(" getUserGroupMembers - gets members of a user group (Note: it is possible for a group to have no members)") print(" parameters:") print(" -n name - name of an existing user group") print(" -a affiliation - affiliation of user group\n") if command == 'all' or command == 'addUsersToGroup': print(" addUsersToGroup - adds users to a group") print(" parameters:") print(" -n name - name of an existing user group") print(" -a affiliation - affiliation of user group") print(" At least one of these must also be specified:") print(" -l UserList - comma delimited list of users to add (no spaces) in username@affiliation form") print(" -f filename - name of file containing users to add (one user per line) in username@affiliation form\n") if command == 'all' or command == 'removeUsersFromGroup': print(" removeUsersFromGroup - removes users from a group") print(" parameters:") print(" -n name - name of an existing user group") print(" -a affiliation - affiliation of user group") print(" At least one of these must also be specified:") print(" -l UserList - comma delimited list of users to remove (no spaces) in username@affiliation form") print(" -f filename - name of file containing users to remove (one user per line) in username@affiliation form\n") if command == 'all' or command == 'emptyGroupMembership': print(" emptyGroupMembership - removes all users currently in a group") print(" parameters:") print(" -n name - name of an existing user group") print(" -a affiliation - affiliation of user group\n") if len(sys.argv) == 1: printHelp() sys.exit(2) testargs = ' '.join(sys.argv[1:]) m = re.search('(-u|-p|-r).*(addUserGroup|getUserGroupAttributes|deleteUserGroup|editUserGroup|getUserGroupMembers|addUsersToGroup|removeUsersFromGroup|emptyGroupMembership)', testargs) if m != None: parser1 = OptionParser(add_help_option=False) parser1.add_option('-u', dest='vcluser') parser1.add_option('-p', type="string", dest='vclpass') parser1.add_option('-r', dest='vclurl') command = m.group(2) last = sys.argv.index(command) (options, remaining) = parser1.parse_args(sys.argv[1:last]) args = sys.argv[(last + 1):] if options.vcluser: vcluser = options.vcluser if options.vclpass: vclpass = options.vclpass if options.vclurl: vclurl = options.vclurl else: args = sys.argv[2:] command = sys.argv[1] try: vcluser except NameError: print("ERROR: vcluser must be definied internally or specified on the command line") sys.exit(1) try: vclpass except NameError: print("ERROR: vclpass must be definied internally or specified on the command line") sys.exit(1) try: vclurl except NameError: print("ERROR: vclurl must be definied internally or specified on the command line") sys.exit(1) parser = OptionParser(add_help_option=False) # addUserGroup if command == 'addUserGroup': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') parser.add_option('-o', dest='owner') parser.add_option('-m', dest='managingGroup') parser.add_option('-i', dest='initialmax') parser.add_option('-t', dest='totalmax') parser.add_option('-x', dest='maxextend') parser.add_option('-c', dest='custom', default=1) (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('addUserGroup') sys.exit(2) if options.custom == '1' and options.name and options.affiliation and options.owner and options.managingGroup and options.initialmax and options.totalmax and options.maxextend: pass elif options.custom == '0' and options.name and options.affiliation and options.initialmax and options.totalmax and options.maxextend: options.owner = '' options.managingGroup = '' else: print("ERROR: Incorrect number of parameters specified\n") printHelp('addUserGroup') sys.exit(2) options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl addUserGroup(options) # getUserGroupAttributes elif command == 'getUserGroupAttributes': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('getUserGroupAttributes') sys.exit(2) if options.name and options.affiliation: pass else: print("ERROR: Incorrect number of parameters specified\n") printHelp('getUserGroupAttributes') sys.exit(2) options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl getUserGroupAttributes(options) # deleteUserGroup elif command == 'deleteUserGroup': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('deleteUserGroup') sys.exit(2) if options.name and options.affiliation: pass else: print("ERROR: Incorrect number of parameters specified\n") printHelp('deleteUserGroup') sys.exit(2) options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl deleteUserGroup(options) # editUserGroup elif command == 'editUserGroup': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') parser.add_option('-N', dest='newname') parser.add_option('-A', dest='newaffiliation') parser.add_option('-O', dest='owner') parser.add_option('-M', dest='managingGroup') parser.add_option('-I', dest='initialmax') parser.add_option('-T', dest='totalmax') parser.add_option('-X', dest='maxextend') (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('editUserGroup') sys.exit(2) if options.name and options.affiliation: pass else: print("ERROR: Incorrect number of parameters specified\n") printHelp('editUserGroup') sys.exit(2) params = "name: %s\naffiliation: %s" % (options.name, options.affiliation) if options.newname: params += "\nnewname: " + options.newname else: options.newname = '' if options.newaffiliation: params += "\nnewaffiliation: " + options.newaffiliation else: options.newaffiliation = '' if options.owner: params += "\nowner: " + options.owner else: options.owner = '' if options.managingGroup: params += "\nmanagingGroup: " + options.managingGroup else: options.managingGroup = '' if options.initialmax: params += "\ninitialmax: " + options.initialmax else: options.initialmax = '' if options.totalmax: params += "\ntotalmax: " + options.totalmax else: options.totalmax = '' if options.maxextend: params += "\nmaxextend: " + options.maxextend else: options.maxextend = '' options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl editUserGroup(options) # getUserGroupMembers elif command == 'getUserGroupMembers': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('getUserGroupMembers') sys.exit(2) if options.name and options.affiliation: pass else: print("ERROR: Incorrect number of parameters specified\n") printHelp('getUserGroupMembers') sys.exit(2) options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl getUserGroupMembers(options) # addUsersToGroup elif command == 'addUsersToGroup': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') parser.add_option('-l', dest='userlist') parser.add_option('-f', dest='filename') (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('addUsersToGroup') sys.exit(2) if options.name and options.affiliation: pass else: print("ERROR: Incorrect number of parameters specified\n") printHelp('addUsersToGroup') sys.exit(2) if options.userlist: pass elif options.filename: # check for file existance if not os.path.exists(options.filename): print("ERROR: specified file (%s) does not exist" % (options.filename)) sys.exit(3) # check for file readability perm = os.stat(options.filename) mode = perm[stat.ST_MODE] if not mode & stat.S_IREAD: print("ERROR: specified file (%s) is not readable" % (options.filename)) sys.exit(3) else: print("ERROR: at least one of -l or -f must be specified\n") printHelp('addUsersToGroup') sys.exit(2) options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl addUsersToGroup(options) # removeUsersFromGroup elif command == 'removeUsersFromGroup': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') parser.add_option('-l', dest='userlist') parser.add_option('-f', dest='filename') (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('removeUsersFromGroup') sys.exit(2) if options.name and options.affiliation: pass else: print("ERROR: Incorrect number of parameters specified\n") printHelp('removeUsersFromGroup') sys.exit(2) if options.userlist: pass elif options.filename: # check for file existance if not os.path.exists(options.filename): print("ERROR: specified file (%s) does not exist" % (options.filename)) sys.exit(3) # check for file readability perm = os.stat(options.filename) mode = perm[stat.ST_MODE] if not mode & stat.S_IREAD: print("ERROR: specified file (%s) is not readable" % (options.filename)) sys.exit(3) else: print("ERROR: at least one of -l or -f must be specified\n") printHelp('removeUsersFromGroup') sys.exit(2) options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl removeUsersFromGroup(options) # emptyGroupMembership elif command == 'emptyGroupMembership': parser.add_option('-n', dest='name') parser.add_option('-a', dest='affiliation') (options, remaining) = parser.parse_args(args) if len(remaining): print("ERROR: Incorrect number of parameters specified\n") printHelp('emptyGroupMembership') sys.exit(2) if options.name and options.affiliation: pass else: print("ERROR: Incorrect number of parameters specified\n") printHelp('emptyGroupMembership') sys.exit(2) options.vcluser = vcluser options.vclpass = vclpass options.vclurl = vclurl emptyGroupMembership(options) else: print("ERROR: unknown command: %s\n" % (command)) printHelp() sys.exit(2) sys.exit(0)