class CLIClient: def __init__(self, appid = "INDX CLIClient"): """ Associate command-line actions with functions, and enforce required variables. """ self.actions = {'create_box': {'f': self.create_box, 'args': ['box']}, 'delete_box': {'f': self.delete_box, 'args': ['box']}, 'list_boxes': {'f': self.list_boxes, 'args': []}, 'get_object_ids': {'f': self.get_object_ids, 'args': ['box']}, 'create_user': {'f': self.create_user, 'args': ['target_username','target_password']}, 'update': {'f': self.update, 'args': ['box','data','version']}, 'delete': {'f': self.delete, 'args': ['box','id','version']}, 'get_latest': {'f': self.get_latest, 'args': ['box']}, 'get_by_ids': {'f': self.get_by_ids, 'args': ['box','id']}, 'query': {'f': self.query, 'args': ['box','query']}, 'diff': {'f': self.diff, 'args': ['box','from','return_objs']}, #'listen': {'f': self.listen, 'args': ['box']}, 'add_file': {'f': self.add_file, 'args': ['box','data','id','version','contenttype']}, 'delete_file': {'f': self.delete_file, 'args': ['box','id','version']}, 'get_file': {'f': self.get_file, 'args': ['box','id']}, 'list_files': {'f': self.list_files, 'args': ['box']}, 'set_acl': {'f': self.set_acl, 'args': ['box','acl','target_username']}, 'get_acls': {'f': self.get_acls, 'args': ['box']}, 'generate_new_key': {'f': self.generate_new_key, 'args': ['box']}, 'create_root_box': {'f': self.create_root_box, 'args': ['box']}, 'link_remote_box': {'f': self.link_remote_box, 'args': ['box', 'remote_token', 'remote_box', 'remote_address']}, 'generate_token': {'f': self.generate_token, 'args': ['box']}, } self.token = None self.appid = appid self.indx = None def set_args(self, args): """ Move relevant command-line arguments into local variable. """ logging.debug("Set args: {0}".format(args)) self.args = args """ Flatten single value lists into flat key/value pair. """ for key in args: if type(args[key]) == type([]) and len(args[key]) == 1: args[key] = args[key][0] """ Ensure self.server always ends in a / """ if self.args['server'][-1:] != "/": self.args['server'] += "/" def check_args(self, required): not_set = [] for key in required: if key not in self.args or self.args[key] is None or self.args[key] == "": not_set.append(key) if not self.args['allowempty'] and len(not_set) > 0: raise Exception("The following values cannot be empty for this action: {0}".format(", ".join(not_set))) def auth_and_get_token(self, get_token): """ Authenticate, get a token and call it back to the deferred. """ return_d = Deferred() def authed_cb(): def token_cb(token): if token is not None: self.token = token return_d.callback(token) else: return_d.callback(None) if get_token: authclient.get_token(self.args['box']).addCallbacks(token_cb, return_d.errback) else: token_cb(None) authclient = IndxClientAuth(self.args['server'], self.appid) self.client = authclient.client authclient.auth_plain(self.args['username'], self.args['password']).addCallbacks(lambda response: authed_cb(), return_d.errback) return return_d def call_action(self, name, *args, **kwargs): """ Calls an action by name. """ return_d = Deferred() action = self.actions[name] f = action['f'] self.check_args(action['args']) def do_call(): f(*args, **kwargs).addCallbacks(lambda status: return_d.callback(self.parse_status(name, status)), return_d.errback) if not self.token: def token_cb(token): if not self.indx: self.indx = IndxClient(self.args['server'], self.args['box'], self.appid, token = token, client = self.client) do_call() self.auth_and_get_token(IndxClient.requires_token(f)).addCallbacks(token_cb, return_d.errback) else: do_call() return return_d def parse_status(self, source, status): """ Parse the status returned from the server, and raise an Exception if necessary. """ if status is not None: # status is None when a file has been printed raw if status['code'] < 200 or status['code'] > 299: raise Exception("{0} in box {1} failed. Response is {2} with code {3}".format(source, self.args['box'], status['message'], status['code'])) else: if "data" in status and self.args['jsondata']: return json.dumps(status['data'], indent = 2) else: pretty = pprint.pformat(status, indent=2, width=80) logging.info("{0} in box {1} successful, return is: {2}".format(source, self.args['box'], pretty)) return None """ Test functions.""" def create_box(self): """ Test to create a box. """ logging.debug("Creating box: '{0}' on server '{1}'".format(self.args['box'], self.args['server'])) return self.indx.create_box() def delete_box(self): """ Test to delete a box. """ logging.debug("Deleting box: '{0}' on server '{1}'".format(self.args['box'], self.args['server'])) return self.indx.delete_box() def list_boxes(self): """ List the boxes on the INDX server. """ logging.debug("Listing boxes on server '{0}'".format(self.args['server'])) return self.indx.list_boxes() def get_object_ids(self): """ Get the IDs of every object in this box. """ logging.debug("Getting a list of object IDs on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) return self.indx.get_object_ids() def update(self): """ Test to update objects in a box. """ logging.debug("Updating data to box: '{0}' on server '{1}'".format(self.args['box'], self.args['server'])) #return self.indx.update(self.args['version'], cjson.decode(self.args['data'].read()), all_unicode=True) return self.indx.update(self.args['version'], json.loads(self.args['data'].read())) def delete(self): """ Test to delete objects from a box. 'id' argument should be a list of ids e.g., --id=id1 --id=id2 """ logging.debug("Deleting data to box: '{0}' on server '{1}'".format(self.args['box'], self.args['server'])) return self.indx.delete(self.args['version'], self.args['id']) def get_latest(self): """ Get the latest version of every object in this box. """ logging.debug("Getting latest objects on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) return self.indx.get_latest() def get_by_ids(self): """ Get the latest version of specific objects in this box. """ logging.debug("Getting latest objects on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) ids = self.args['id'] if type(ids) != type([]): ids = [ids] # put a single id into an array return self.indx.get_by_ids(ids) def query(self): """ Query this box. e.g., query="{ '@id': 2983 }" or query="{ 'firstname': 'dan' }" """ logging.debug("Querying server '{0}' in box '{1}' with depth '{2}'".format(self.args['server'], self.args['box'], self.args['depth'])) return self.indx.query(self.args['query'], depth = int(self.args['depth'])) def diff(self): """ Run a diff of two version of this box. """ logging.debug("Calling diff on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) to_version = None if "to" in self.args and self.args['to'] is not None: to_version = self.args['to'] return self.indx.diff(self.args['return_objs'], self.args['from'], to_version = to_version) # def listen(self): # """ Listen to updates to the database (locally, not with HTTP) and print out the diff in realtime. """ # self.check_args(['box', 'username', 'password']) # # def observer(notify): # print "Version updated to: {0}".format(notify.payload) # # def err_cb(failure): # logging.error("Error in test listen: {0}".format(failure)) # reactor.stop() # # def connected_cb(conn): # print "Listening..." # conns = {"conn": conn} # store = ObjectStoreAsync(conns, self.args['username'], self.appid, "127.0.0.1") # TODO get the IP a better way? does it matter here? # store.listen(observer) # # d = database.connect_box_raw(self.args['box'], self.args['username'], self.args['password']) # d.addCallbacks(connected_cb, err_cb) # reactor.run() def add_file(self): """ Add a file to the database. """ logging.debug("Adding a file to server '{0}' in box '{1}', with id: {2}".format(self.args['server'], self.args['box'], self.args['id'])) return self.indx.add_file(self.args['version'], self.args['id'], self.args['data'].read(), self.args['contenttype']) def delete_file(self): """ Delete a file from the database. """ logging.debug("Deleting a file from server '{0}' in box '{1}', with id: {2}".format(self.args['server'], self.args['box'], self.args['id'])) return self.indx.delete_file(self.args['version'], self.args['id']) def get_file(self): """ Get a file from the database. """ logging.debug("Calling get_file on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) # returns the file to stdout so it can be piped to a file print self.indx.get_file(self.args['id']) def list_files(self): """ Get a list of the files from the database. """ logging.debug("Calling list_files on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) return self.indx.list_files() def set_acl(self): """ Set an ACL for a target user for a database. """ logging.debug("Calling set_acl on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) return self.indx.set_acl(self.args['acl'], self.args['target_username']) def get_acls(self): """ Get ACLs for a database. """ logging.debug("Calling get_acls on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) return self.indx.get_acls() def generate_new_key(self): """ Generate and store a new key, returning the public and public-hash parts of the key. """ logging.debug("Calling generate_new_key on server '{0}' in box '{1}'".format(self.args['server'], self.args['box'])) return self.indx.generate_new_key() def create_root_box(self): """ Create root box for a user. """ logging.debug("Calling create_root_box on server '{0}' for box '{1}'".format(self.args['server'], self.args['box'])) return self.indx.create_root_box(self.args['box']) def create_user(self): """ Create a new user. """ logging.debug("Calling create_user on server '{0}' with target username '{1}'".format(self.args['server'], self.args['target_username'])) return self.indx.create_user(self.args['target_username'], self.args['target_password']) def link_remote_box(self): """ Link a remote box with a local box. """ logging.debug("Calling link_remote_box on remote_address '{0}', remote_box '{1}', remote_token '{2}'".format(self.args['remote_address'], self.args['remote_box'], self.args['remote_token'])) return self.indx.link_remote_box(self.args['remote_address'], self.args['remote_box'], self.args['remote_token']) def generate_token(self): """ Generate a token for this box, and print it. """ logging.debug("Calling generate_token on box {0}.".format(self.args['box'])) return_d = Deferred() def token_cb(token): return_d.callback({"code": 200, "data": token}) self.auth_and_get_token(True).addCallbacks(token_cb, return_d.errback) return return_d
class CLIClient: def __init__(self, appid="INDX CLIClient"): """ Associate command-line actions with functions, and enforce required variables. """ self.actions = { 'create_box': { 'f': self.create_box, 'args': ['box'] }, 'delete_box': { 'f': self.delete_box, 'args': ['box'] }, 'list_boxes': { 'f': self.list_boxes, 'args': [] }, 'get_object_ids': { 'f': self.get_object_ids, 'args': ['box'] }, 'create_user': { 'f': self.create_user, 'args': ['target_username', 'target_password'] }, 'update': { 'f': self.update, 'args': ['box', 'data', 'version'] }, 'update_json': { 'f': self.update_json, 'args': ['box', 'data', 'version'] }, 'update_raw': { 'f': self.update_raw, 'args': ['box', 'data', 'version'] }, 'delete': { 'f': self.delete, 'args': ['box', 'id', 'version'] }, 'get_latest': { 'f': self.get_latest, 'args': ['box'] }, 'get_by_ids': { 'f': self.get_by_ids, 'args': ['box', 'id'] }, 'query': { 'f': self.query, 'args': ['box', 'query'] }, 'diff': { 'f': self.diff, 'args': ['box', 'from', 'return_objs'] }, #'listen': {'f': self.listen, 'args': ['box']}, 'add_file': { 'f': self.add_file, 'args': ['box', 'data', 'id', 'version', 'contenttype'] }, 'delete_file': { 'f': self.delete_file, 'args': ['box', 'id', 'version'] }, 'get_file': { 'f': self.get_file, 'args': ['box', 'id'] }, 'list_files': { 'f': self.list_files, 'args': ['box'] }, 'set_acl': { 'f': self.set_acl, 'args': ['box', 'acl', 'target_username'] }, 'set_acl_public': { 'f': self.set_acl_public, 'args': ['box', 'acl'] }, 'get_acls': { 'f': self.get_acls, 'args': ['box'] }, 'generate_new_key': { 'f': self.generate_new_key, 'args': ['box'] }, 'create_root_box': { 'f': self.create_root_box, 'args': ['box'] }, 'link_remote_box': { 'f': self.link_remote_box, 'args': ['box', 'remote_token', 'remote_box', 'remote_address'] }, 'generate_token': { 'f': self.generate_token, 'args': ['box'] }, } self.token = None self.appid = appid self.indx = None def set_args(self, args): """ Move relevant command-line arguments into local variable. """ logging.debug("Set args: {0}".format(args)) self.args = args """ Flatten single value lists into flat key/value pair. """ for key in args: if type(args[key]) == type([]) and len(args[key]) == 1: args[key] = args[key][0] """ Ensure self.server always ends in a / """ if self.args['server'][-1:] != "/": self.args['server'] += "/" def check_args(self, required): not_set = [] for key in required: if key not in self.args or self.args[key] is None or self.args[ key] == "": not_set.append(key) if not self.args['allowempty'] and len(not_set) > 0: raise Exception( "The following values cannot be empty for this action: {0}". format(", ".join(not_set))) def auth_and_get_token(self, get_token): """ Authenticate, get a token and call it back to the deferred. """ return_d = Deferred() def authed_cb(): def token_cb(token): if token is not None: self.token = token return_d.callback(token) else: return_d.callback(None) if get_token: authclient.get_token(self.args['box']).addCallbacks( token_cb, return_d.errback) else: token_cb(None) authclient = IndxClientAuth(self.args['server'], self.appid) self.client = authclient.client authclient.auth_plain(self.args['username'], self.args['password']).addCallbacks( lambda response: authed_cb(), return_d.errback) return return_d def call_action(self, name, *args, **kwargs): """ Calls an action by name. """ return_d = Deferred() action = self.actions[name] f = action['f'] self.check_args(action['args']) def do_call(): f(*args, **kwargs).addCallbacks( lambda status: return_d.callback( self.parse_status(name, status)), return_d.errback) if not self.token: def token_cb(token): if not self.indx: self.indx = IndxClient(self.args['server'], self.args['box'], self.appid, token=token, client=self.client) do_call() self.auth_and_get_token(IndxClient.requires_token(f)).addCallbacks( token_cb, return_d.errback) else: do_call() return return_d def parse_status(self, source, status): """ Parse the status returned from the server, and raise an Exception if necessary. """ if status is not None: # status is None when a file has been printed raw if status['code'] < 200 or status['code'] > 299: raise Exception( "{0} in box {1} failed. Response is {2} with code {3}". format(source, self.args['box'], status['message'], status['code'])) else: if "data" in status and self.args['jsondata']: return json.dumps(status['data'], indent=2) else: pretty = pprint.pformat(status, indent=2, width=80) logging.info( "{0} in box {1} successful, return is: {2}".format( source, self.args['box'], pretty)) return None """ Test functions.""" def create_box(self): """ Test to create a box. """ logging.debug("Creating box: '{0}' on server '{1}'".format( self.args['box'], self.args['server'])) return self.indx.create_box() def delete_box(self): """ Test to delete a box. """ logging.debug("Deleting box: '{0}' on server '{1}'".format( self.args['box'], self.args['server'])) return self.indx.delete_box() def list_boxes(self): """ List the boxes on the INDX server. """ logging.debug("Listing boxes on server '{0}'".format( self.args['server'])) return self.indx.list_boxes() def get_object_ids(self): """ Get the IDs of every object in this box. """ logging.debug( "Getting a list of object IDs on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.get_object_ids() def update(self): """ Test to update objects in a box. """ logging.debug("Updating data to box: '{0}' on server '{1}'".format( self.args['box'], self.args['server'])) #return self.indx.update(self.args['version'], cjson.decode(self.args['data'].read()), all_unicode=True) return self.indx.update(self.args['version'], json.loads(self.args['data'].read())) def update_json(self): """ Test to update json objects in a box. """ logging.debug("Updating data to box: '{0}' on server '{1}'".format( self.args['box'], self.args['server'])) #return self.indx.update(self.args['version'], cjson.decode(self.args['data'].read()), all_unicode=True) return self.indx.update_json(self.args['version'], json.loads(self.args['data'].read())) def update_raw(self): """ Test to update raw objects in a box. """ logging.debug("Updating raw data to box: '{0}' on server '{1}'".format( self.args['box'], self.args['server'])) #return self.indx.update(self.args['version'], cjson.decode(self.args['data'].read()), all_unicode=True) return self.indx.update_raw(self.args['version'], json.loads(self.args['data'].read())) def delete(self): """ Test to delete objects from a box. 'id' argument should be a list of ids e.g., --id=id1 --id=id2 """ logging.debug("Deleting data to box: '{0}' on server '{1}'".format( self.args['box'], self.args['server'])) return self.indx.delete(self.args['version'], self.args['id']) def get_latest(self): """ Get the latest version of every object in this box. """ logging.debug( "Getting latest objects on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.get_latest() def get_by_ids(self): """ Get the latest version of specific objects in this box. """ logging.debug( "Getting latest objects on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) ids = self.args['id'] if type(ids) != type([]): ids = [ids] # put a single id into an array return self.indx.get_by_ids(ids) def query(self): """ Query this box. e.g., query="{ '@id': 2983 }" or query="{ 'firstname': 'dan' }" """ logging.debug( "Querying server '{0}' in box '{1}' with depth '{2}'".format( self.args['server'], self.args['box'], self.args['depth'])) return self.indx.query(self.args['query'], depth=int(self.args['depth'])) def diff(self): """ Run a diff of two version of this box. """ logging.debug("Calling diff on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) to_version = None if "to" in self.args and self.args['to'] is not None: to_version = self.args['to'] return self.indx.diff(self.args['return_objs'], self.args['from'], to_version=to_version) # def listen(self): # """ Listen to updates to the database (locally, not with HTTP) and print out the diff in realtime. """ # self.check_args(['box', 'username', 'password']) # # def observer(notify): # print "Version updated to: {0}".format(notify.payload) # # def err_cb(failure): # logging.error("Error in test listen: {0}".format(failure)) # reactor.stop() # # def connected_cb(conn): # print "Listening..." # conns = {"conn": conn} # store = ObjectStoreAsync(conns, self.args['username'], self.appid, "127.0.0.1") # TODO get the IP a better way? does it matter here? # store.listen(observer) # # d = database.connect_box_raw(self.args['box'], self.args['username'], self.args['password']) # d.addCallbacks(connected_cb, err_cb) # reactor.run() def add_file(self): """ Add a file to the database. """ logging.debug( "Adding a file to server '{0}' in box '{1}', with id: {2}".format( self.args['server'], self.args['box'], self.args['id'])) return self.indx.add_file(self.args['version'], self.args['id'], self.args['data'].read(), self.args['contenttype']) def delete_file(self): """ Delete a file from the database. """ logging.debug( "Deleting a file from server '{0}' in box '{1}', with id: {2}". format(self.args['server'], self.args['box'], self.args['id'])) return self.indx.delete_file(self.args['version'], self.args['id']) def get_file(self): """ Get a file from the database. """ logging.debug("Calling get_file on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) # returns the file to stdout so it can be piped to a file print self.indx.get_file(self.args['id']) def list_files(self): """ Get a list of the files from the database. """ logging.debug("Calling list_files on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.list_files() def set_acl(self): """ Set an ACL for a target user for a database. """ logging.debug("Calling set_acl on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.set_acl(self.args['acl'], self.args['target_username']) def set_acl_public(self): """ Set an ACL for the public uer for a database. """ logging.debug( "Calling set_acl_public on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.set_acl_public(self.args['acl']) def get_acls(self): """ Get ACLs for a database. """ logging.debug("Calling get_acls on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.get_acls() def generate_new_key(self): """ Generate and store a new key, returning the public and public-hash parts of the key. """ logging.debug( "Calling generate_new_key on server '{0}' in box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.generate_new_key() def create_root_box(self): """ Create root box for a user. """ logging.debug( "Calling create_root_box on server '{0}' for box '{1}'".format( self.args['server'], self.args['box'])) return self.indx.create_root_box(self.args['box']) def create_user(self): """ Create a new user. """ logging.debug( "Calling create_user on server '{0}' with target username '{1}'". format(self.args['server'], self.args['target_username'])) return self.indx.create_user(self.args['target_username'], self.args['target_password']) def link_remote_box(self): """ Link a remote box with a local box. """ logging.debug( "Calling link_remote_box on remote_address '{0}', remote_box '{1}', remote_token '{2}'" .format(self.args['remote_address'], self.args['remote_box'], self.args['remote_token'])) return self.indx.link_remote_box(self.args['remote_address'], self.args['remote_box'], self.args['remote_token']) def generate_token(self): """ Generate a token for this box, and print it. """ logging.debug("Calling generate_token on box {0}.".format( self.args['box'])) return_d = Deferred() def token_cb(token): return_d.callback({"code": 200, "data": token}) self.auth_and_get_token(True).addCallbacks(token_cb, return_d.errback) return return_d