class UpdateMetadataServer(Method): """ Update a metadata server. A user can only update a metadata server that he/she owns. An admin can update any metadata server. If the server is running, this method restarts it on successful application of the updated fields """ accepts = [ Auth(), Mixed(MDServer.fields['server_id'], MDServer.fields['name']), dict([(n, MDServer.fields[n]) for n in ['auth_read', 'auth_write', 'portnum']]) ] roles = ['admin', 'user'] returns = Parameter(int, "1 if successful; negative error code otherwise") def load_mdserver(self, mdserver_name_or_id): mdserver = None try: mdservers = [] if isinstance(mdserver_name_or_id, str): mdservers = MDServers(self.api, {'name': mdserver_name_or_id}) else: mdservers = MDServers(self.api, {'server_id': mdserver_name_or_id}) mdserver = mdservers[0] except Exception, e: raise MDObjectNotFound("MDServer(%s)" % mdserver_name_or_id) return mdserver
class GetContentServers( Method ): """ Read zero or more content servers. If no filter is given, every content server added by the caller is read. Admins can see all content servers; users can only see their own """ accepts = [ Auth(), Parameter( dict, "Fields to filter on", nullok = True ), Parameter( [str], "List of fields to return", nullok = True ) ] roles = ['admin','user'] returns = [Content.fields] """ def call(self, auth, content_filter, return_fields): assert self.caller is not None # ask the CDN for the content content = self.api.cdn.get_content( content_filter, return_fields ) return content """ def call(self, auth, content_filter, return_fields): assert self.caller is not None roles = self.caller['roles'] if not content_filter: content_filter = {} if not return_fields: return_fields = [name for (name,param) in Content.fields.items()] users = Users(self.api, {'username': auth['Username']}) user = users[0] if content_filter.get('owner') != None and content_filter['owner'] != user['user_id'] and (self.caller == None or 'admin' not in roles): raise MDUnauthorized( "User(%s) is not allowed to read content servers from User(%s)" % (user['username'], content_filter['owner']) ) # if the caller isn't an admin, then only read content servers with the caller's user id if 'admin' not in roles: content_filter['owner'] = user['user_id'] contents = Contents( self.api, content_filter ) # remove non-specified return fields ret = [] for c in contents: c_dict = {} for rf in return_fields: if rf in c: c_dict[rf] = c[rf] ret.append( c_dict ) return ret
class AddUserToMetadataServer( Method ): """ Add a user to a metadata server. The caller must be the owner of the metadata server, or an admin. """ accepts = [ Auth(), Mixed(Parameter(int, "User ID of the user who will be added to this server"), Parameter(str, "Username of the user who will be added to this server")), Mixed(MDServer.fields['server_id'], MDServer.fields['name']) ] roles = ['admin','user'] returns = Parameter(int, "1 if successful; otherwise a negative error code from failing to update the metadata server") def call(self, auth, username_or_id, metadata_server_name_or_id): assert self.caller is not None roles = self.caller['roles'] owner = None try: owners = Users(self.api, {'username': auth['Username']}) owner = owners[0] except: raise MDObjectNotFound( auth['Username'], "Caller not found" ) # look up this metadata server mdservers = None mdserver = None try: if isinstance( metadata_server_name_or_id, int ): mdservers = MDServers( self.api, {'server_id': metadata_server_name_or_id} ) else: mdservers = MDServers( self.api, {'name': metadata_server_name_or_id} ) mdserver = mdservers[0] except Exception, e: raise MDObjectNotFound( 'MDServer(%s)' % (metadata_server_name_or_id), str(e)) # make sure this server is owned by the caller, or that the caller is an admin if 'admin' not in roles and mdserver['owner'] != owner['user_id']: raise MDUnauthorized( "User(%s) is not the owner of MDServer(%s)" % (owner['username'], metadata_server_name_or_id) ) # look up this user to be added users = None user = None try: if isinstance(username_or_id, str): users = Users( self.api, {'username':username_or_id, 'enabled': True}) else: users = Users( self.api, {'user_id':username_or_id, 'enabled': True}) user = users[0] except Exception, e: raise MDObjectNotFound( 'User(%s)' % (username_or_id), str(e) )
class DeleteUser(Method): """ Remove a user account. A user may remove himself/herself, but no one else. An admin may remove anyone. All of the user's registered content and metadata servers will be removed as well. The user will be removed from all metadata servers. """ accepts = [Auth(), Mixed(User.fields['user_id'], User.fields['username'])] roles = ['admin', 'user'] returns = Parameter(int, "1 if successful") def call(self, auth, username_or_id): assert self.caller is not None roles = self.caller['roles'] user = None try: if isinstance(username_or_id, str): users = Users(self.api, {'username': username_or_id}) else: users = Users(self.api, {'user_id': username_or_id}) user = users[0] except Exception, e: raise MDObjectNotFound('User(%s)' % (username_or_id), str(e)) # user can only delete himself/herself, unless admin if ('admin' not in roles) and user['username'] != auth['Username']: raise MDUnauthorized("User(%s) cannot be deleted by User(%s)" % (username_or_id, auth['Username'])) # unregister this user from every metadata server it's subscribed to for md_id in user['sub_mdserver_ids']: md = None try: mds = MDServers(self.api, {'server_id': md_id}) md = mds[0] except: continue try: md.remove_user(user) except MDException, mde: raise MDMethodFailed( "Could not remove User(%s) from MDServer(%s)" % (user['username'], md_id), mde) except Exception, e: raise MDMethodFailed( "Failed to destroy User(%s)" % username_or_id, str(e))
class GetMetadataFromServer(Method): """ Get metadata from a running metadata server, given its URL, ID, or (hostname, portnum) tuple """ accepts = [ Auth(), Mixed(Mixed(MDServer.fields['host'], MDServer.fields['portnum']), Parameter(str, "Metadata server URL"), Parameter(int, "Metadata server ID")) ] roles = ['user', 'admin'] returns = Parameter([str], "List of metadata entries as strings") def call(self, auth, host_portnum_or_url_or_id): assert self.caller is not None md_server_url = None if isinstance(host_portnum_or_url_or_id, list) or isinstance( host_portnum_or_url_or_id, tuple): md_server_url = host_portnum_or_url_or_id[0] + '/' + str( host_portnum_or_url_or_id[1]) elif isinstance(host_portnum_or_url_or_id, str): md_server_url = host_portnum_or_url_or_id else: # look up metadata server and get its URL mdservers = MDServers({'server_id': host_portnum_or_url_or_id}) if len(mdservers) <= 0: # not found raise MDObjectNotFound('mdserver', host_portnum_or_url_or_id) # perform a GET on the url, with the caller's credentials auth_header = urllib2.HTTPBasicAuthHandler() auth_header.add_password(realm=None, uri=md_server_url, user=auth.get('Username'), passwd=auth.get('AuthString')) opener = urllib2.build_opener(auth_header) urllib2.install_opener(opener) metadata = None try: md_handle = urllib2.urlopen(md_server_url) metadata = md_handle.read() md_handle.close() except Exception, e: raise MDMethodFailed("Could not open '%s'" % (md_server_url), e) return metadata.split("\n")[:-1]
class DeleteMetadataServer(Method): """ Delete a metadata server. This will also stop it. Unregister all other users from this server. The caller can delete only their own metadata servers, unless they are an admin. """ accepts = [ Auth(), Mixed(MDServer.fields['name'], MDServer.fields['server_id']) ] roles = ['admin', 'user'] returns = Parameter( int, "1 if successful; otherwise a negative error code resulting from a failure to shut down the metadata server" ) def call(self, auth, mdserver_name_or_id): assert self.caller is not None roles = self.caller['roles'] # look up the mdserver md = None try: if isinstance(mdserver_name_or_id, str): mds = MDServers(self.api, {'name': mdserver_name_or_id}) else: mds = MDServers(self.api, {'server_id': mdserver_name_or_id}) md = mds[0] except Exception, e: raise MDObjectNotFound("MDServer(%s)" % (mdserver_name_or_id), str(e)) # look up the user user = None try: user_identifier = None if 'admin' not in roles: users = Users(self.api, {'username': auth['Username']}) user_identifier = auth['Username'] else: users = Users(self.api, {'user_id': md['owner']}) user_identifier = md['owner'] user = users[0] except Exception, e: raise MDObjectNotFound("User(%s)" % user_identifier, str(e))
class AddContentServer(Method): """ Add a content server under the control of a user. Register the server on the underlying CDN as well. The owner of this content will be the user that calls this method. """ accepts = [Auth(), dict([(k, Content.fields[k]) for k in ['host_url']])] roles = ['admin', 'user'] returns = Parameter(int, "The content's ID (positive number) if successful") def call(self, auth, content_fields): assert self.caller is not None users = Users(self.api, {'username': auth['Username']}) user = users[0] # how many contents has this user created? num_contents = len(user['content_ids']) if num_contents > user['max_contents']: raise MDResourceExceeded('User(%s)' % (user['username']), 'content server') content_fields['owner'] = user['user_id'] # register this content on the CDN remote_content_id = self.api.cdn.add_content( user, content_fields['host_url']) if remote_content_id == None or remote_content_id < 0: raise MDInternalError( "AddContentServer: CDN could not add '%s' to the CDN with user '%s'" % (content_fields.get('host_url'), content_fields.get('username'))) c = Content(self.api, content_fields) c['content_id'] = remote_content_id c.sync(insert=True) user.add_content(c) # will synchronize itself user.sync() return c['content_id']
class StartMetadataServer(Method): """ Activate an existing metadata server. The caller must own the metadata server, unless they are an admin. """ accepts = [ Auth(), Mixed(MDServer.fields['server_id'], MDServer.fields['name']) ] roles = ['admin', 'user'] returns = Parameter( [str], "The read and write URLs of the metadata server (on success)") def call(self, auth, mdserver_name_or_id): roles = [] if self.caller: roles = self.caller['roles'] users = Users(self.api, {'username': auth['Username']}) user = users[0] mdserver = None try: mdservers = None if isinstance(mdserver_name_or_id, str): mdservers = MDServers(self.api, {'name': mdserver_name_or_id}) else: mdservers = MDServers(self.api, {'server_id': mdserver_name_or_id}) mdserver = mdservers[0] except Exception, e: raise MDObjectNotFound("MDServer(%s)", mdserver_name_or_id, e) if (self.caller == None or 'admin' not in roles) and mdserver['owner'] != user['user_id']: raise MDUnauthorized("MDServer(%s) cannot be started by User(%s)" % (mdserver_name_or_id, user['username'])) rc = mdserver.start_server() if not rc: raise MDMetadataServerError("Could not start server") else: # started up! return rc
class DeleteContentServer(Method): """ Delete a content server. This method will work either if this user is an admin or the user owns the content server """ accepts = [Auth(), Content.fields['content_id']] roles = ['admin', 'user'] returns = Parameter( int, "1 if successful; negative error code if the content server could not be unregistered from the CDN" ) def call(self, auth, content_id): assert self.caller is not None roles = self.caller['roles'] # look up user user = None try: users = Users(self.api, {'username': auth['Username']}) user = users[0] except Exception, e: raise MDObjectNotFound("User(%s)" % auth['Username'], str(e)) # sanity check if content_id not in user['content_ids']: raise MDInvalidArgument( "Content(%s) is not owned by User(%s)" % (content_id, auth['Username']), "DeleteContentServer") # look up content content = None try: contents = Contents(self.api, {'content_id': content_id}) content = contents[0] except Exception, e: raise MDObjectNotFound("Content(%s)" % content_id, str(e))
class AddUser(Method): """ Add a user account. The account will be disabled at first. """ accepts = [ Auth(), dict([(n, User.fields[n]) for n in ['username', 'password', 'email']]) ] roles = ['admin'] returns = Parameter(int, "The user's UID (positive number) if successful") def call(self, auth, user_fields): assert self.caller is not None # hash the password before we store it m = SMDS.auth.new_sha1() m.update(user_fields['password']) password_hash = m.hexdigest().lower() user_fields['password'] = password_hash # default constraints on a user's power user_fields['max_mdservers'] = self.api.DEFAULT_MAX_MDSERVERS user_fields['max_contents'] = self.api.DEFAULT_MAX_CONTENTS user_fields['enabled'] = False user_fields['roles'] = ['user'] u = User(self.api, user_fields) # register this user on the CDN rc = self.api.cdn.add_user(u) if rc != 1: raise MDInternalError( "AddUser: could not add User(%s) to the CDN" % (u['username'])) u.sync() return u['user_id']
class GetUsers(Method): """ Get a list of users """ accepts = [ Auth(), Filter(User.fields), Parameter([str], "List of fields to return", nullok=True) ] roles = ['admin', 'user'] returns = [User.fields] def call(self, auth, user_fields, return_fields=None): assert self.caller is not None if 'password' in user_fields: del user_fields['password'] if not return_fields: return_fields = [name for (name, param) in User.fields.items()] if 'password' in return_fields: return_fields.remove('password') users = Users(self.api, user_fields) ret = [] for u in users: u_dict = {} for rf in return_fields: if rf in u.keys(): u_dict[rf] = u[rf] ret.append(u_dict) return ret
class GetMetadataServers(Method): """ Get a list of zero or more extant metadata servers. """ accepts = [ Auth(), Filter(MDServer.fields), Parameter([str], "List of fields to return", nullok=True) ] roles = ['admin', 'user'] returns = [MDServer.fields] def call(self, auth, mdserver_fields, return_fields): assert self.caller is not None roles = self.caller['roles'] users = Users(self.api, {'username': auth['Username']}) user = users[0] if not return_fields: return_fields = [name for (name, param) in MDServer.fields.items()] mdservers = MDServers(self.api, mdserver_fields) ret = [] for md in mdservers: md_dict = {} for rf in return_fields: if rf in md: md_dict[rf] = md[rf] ret.append(md_dict) return ret
class UpdateUser(Method): """ Update a user. A user can only update himself/herself, and only a few fields at that. An admin can update anyone, with some admin-specific fields. """ admin_only_fields = [ 'max_mdservers', 'max_contents', 'enabled', 'roles', 'username' ] accepts = [ Auth(), Mixed(User.fields['username'], User.fields['user_id']), dict([(n, User.fields[n]) for n in set(User.fields.keys()).difference(set(['user_id']))]) ] roles = ['admin', 'user'] returns = Parameter(int, "1 if successful; negative error code otherwise") def call(self, auth, username_or_id, user_fields): assert self.caller is not None roles = self.caller['roles'] # look up the caller user ID calling_users = Users(self.api, {'username': auth['Username']}) calling_user = calling_users[0] # look up this user user = None try: users = None if isinstance(username_or_id, str): users = Users(self.api, {'username': username_or_id}) else: users = Users(self.api, {'user_id': username_or_id}) user = users[0] except Exception, e: raise MDObjectNotFound("User(%s)" % username_or_id, e) # can we update this user? if ('admin' not in roles) and user['user_id'] != calling_user['user_id']: raise MDUnauthorized("User(%s) cannot be updated by User(%s)" % (username_or_id, Auth['Username'])) if 'admin' not in roles: # not an admin, so make sure that the admin-only fields don't change bad_fields = [] for aof in self.admin_only_fields: if aof in user_fields.keys(): bad_fields.append(aof) if len(bad_fields) > 0: raise MDUnauthorized( "Only an admin can update fields %s of User(%s)" % (username_or_id, ", ".join(bad_fields))) if 'password' in user_fields.keys(): # hash the password m = Auth.new_sha1() m.update(user_fields['password']) password_hash = m.hexdigest().lower() user_fields['password'] = password_hash user.update(user_fields) user.sync() return 1
class DeleteUserFromMetadataServer(Method): """ Remove one or more users from a metadata server. The caller is only allowed to call this method on his/her own metadata server; but an admin can call this method on any metadata server. The caller is not allowed to remove a user from the metadata server owned by that user. """ accepts = [ Auth(), Mixed( Parameter(int, "User ID of the user to be removed from this server"), Parameter(str, "Username of the user to be removed from this server"), Parameter([int], "User IDs of the users to be removed from this server")), Mixed(Parameter(int, "Metadata server ID"), Parameter(str, "Metadata name")) ] roles = ['admin', 'user'] returns = Parameter( int, "1 if successful; otherwise a negative error code from failure to update the metadata server" ) def load_mdserver(self, metadata_server_id): mdservers = None if isinstance(metadata_server_id, str): mdservers = MDServers(self.api, {'name': metadata_server_id}) else: mdservers = MDServers(self.api, {'server_id': metadata_server_id}) mdserver = mdservers[0] return mdserver def call(self, auth, username_or_id, metadata_server_id): assert self.caller is not None roles = self.caller['roles'] owners = Users(self.api, {'username': auth['Username']}) owner = owners[0] # look up this metadata server mdservers = None mdserver = None try: mdserver = self.load_mdserver(metadata_server_id) except Exception, e: raise MDObjectNotFound('MDServer(%s)' % (metadata_server_id), str(e)) # make sure this server is owned by the caller, or that the caller is an admin if ('admin' not in roles) and mdserver['owner'] != owner['user_id']: raise MDUnauthorized("User(%s) is not the owner of MDServer(%s)" % (owner['username'], mdserver['server_id'])) # look up this user to be removed users = None user = None remove_many = False try: if isinstance(username_or_id, str): users = Users(self.api, {'username': username_or_id}) elif isinstance(username_or_id, int) or isinstance( username_or_id, long): users = Users(self.api, {'user_id': username_or_id}) elif isinstance(username_or_id, list): users = Users(self.api, username_or_id) remove_many = True user = users[0] except Exception, e: raise MDObjectNotFound('User(%s)' % (username_or_id), str(e))
class AddMetadataServer(Method): """ Add a metadata server. Does not start it, but adds an entry for it in our database. The owner of this metadata server will be the user that calls this method. """ accepts = [ Auth(), dict([(n, MDServer.fields[n]) for n in set(MDServer.fields.keys()).difference( set(['owner', 'status', 'user_ids']))]) ] roles = ['admin', 'user'] returns = Parameter( int, "The metadata server's ID (positive number) if successful") def call(self, auth, mdserver_fields): assert self.caller is not None # look up the user ID users = Users(self.api, {'username': auth['Username']}) user = users[0] # how many metadata servers does this user have? num_md_servers = len(user['my_mdserver_ids']) if num_md_servers > user['max_mdservers']: raise MDResourceExceeded('User(%s)' % user['username'], 'metadata server') mdserver_fields['owner'] = user['user_id'] mdserver_fields['status'] = 'stopped' # url-encode the name, if it exists. Otherwise use a UUID if mdserver_fields.get('name'): mdserver_fields['name'] = urllib2.quote(mdserver_fields['name']) else: mdserver_fields['name'] = urllib2.quote(uuid.uuid4().hex) # authenticate read/write by default if not mdserver_fields.get('auth_read'): mdserver_fields['auth_read'] = True if not mdserver_fields.get('auth_write'): mdserver_fields['auth_write'] = True # get a hostname, if one was not given # if we don't have a host, then pick one if not mdserver_fields.get('host'): host = self.api.next_server_host() mdserver_fields['host'] = host md = MDServer(self.api, mdserver_fields) md.sync() # update join tables user.add_mdserver(md) md.add_user(user) server_id = md['server_id'] # reload md = MDServers(self.api, [server_id])[0] rc = md.create_server() if rc != 1: raise MDMethodFailed( "md.create_server()", "Could not create metadata server, rc = %s" % rc) return md['server_id']