def get_command(remote_ip, **kw): """ @param_post{remote_ip,string} @param_post{kw,dict} keyword params @returns{Command} next command from the que to the asking VM """ vm = VM.get_by_ip(remote_ip) log.debug(0, "Get first command for %s" % vm.id) command = vm.command_set.filter(state=command_states['pending']).order_by('id') if len(command) == 0: return response('ctx_no_command') command = command[0] log.debug(0, "First command is %s" % command.id) command.state = command_states['executing'] command.save() d = command.dict() r = response('ok', d) if int(kw.get('version', 0)) < VERSION: f = file(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'actions.py'), 'r') r['actions_file'] = f.read() f.close() return r
def lookup_by_name(self, first_name, last_name): """ :return: a tuple of (user_id, is_err) If user is found, returns (user_id, False) If user is not found, this is not an error [returns (None, False)] """ if not (first_name and last_name): return response( 400, f'Missing name: "{first_name}" "{last_name}"'), True try: result = self.dynamo.query( ExpressionAttributeValues={ ':fn': { 'S': first_name }, ':ln': { 'S': last_name } }, KeyConditionExpression='first_name = :fn AND last_name = :ln', TableName=self.tablename, IndexName='full_name-index') items = result['Items'] if len(items) == 0: return (None, False) elif len(items) > 1: return response( 500, f'More than one person named "{first_name}" "{last_name}"' ), True else: item = to_json(items[0]) return item['user_id'], False except Exception: raise
def offer_get(event, context): item_id = event['pathParameters']['offer_id'] item = OFFERS.get_by_id(item_id) if item: redact_name(item) return response(200, item) return response(404, 'Item not found')
def user_login(event, context): login_code = event['pathParameters'].get('login_code', None) if not login_code: return response(400, 'Missing login_code parameter') user = USER.lookup_by_login(login_code) if not user: return response(404, 'User not found') return response(200, user)
def user_get(event, context, user=None, admin=None): item_id = event['pathParameters']['user_id'] if not (admin or (user and user['user_id'] == item_id)): return response(401, 'Unauthorized') item = USER.get_by_id(item_id) if item: return response(200, item) return response(404, 'Item not found')
def offer_delete(event, context, admin=None, user=None): item_id = event['pathParameters']['offer_id'] item = OFFERS.get_by_id(item_id) if not item: return response(404, 'Item not found') if not (admin or (user and user['user_id'] == item['user_id'])): # authorized if you're an admin, or if you're the owner of the offer return response(401, 'Unauthorized') success = OFFERS.delete(item_id) return response(200, {'success': success})
def offer_update(event, context, user=None, admin=None): item_id = event['pathParameters']['offer_id'] item = OFFERS.get_by_id(item_id) if not (admin or (user and user['user_id'] == item['user_id'])): # authorized if you're an admin, or if you're the owner of the offer return response(401, 'Unauthorized') if not item: return response(404, 'Item not found') put_data, err = safe_json(event['body']) if err: return put_data if OFFERS.update(item_id, put_data): return response(200, {'result': 'Successfully updated'})
def genericlog(log_enabled, pack_resp, is_user, is_clm_superuser, is_cm_superuser, fun, args, kwargs): """ Generic log is called by actor decorators defined in src.clm.utils.decorators : - src.clm.utils.decorators.guest_log - src.clm.utils.decorators.user_log - src.clm.utils.decorators.admin_cm_log - src.clm.utils.decorators.admin_clm_log It calls decorated functions, additionally performing several tasks. Genericlog performes: -# <i>if decorated function requires user or admin privilidges</i>: <b>authorization</b>; -# <b>execution</b> of the decorated function; -# <b>debug log</b> of the arguments <i>depending on \c log_enabled and function's success</i>; -# <i>if exception is thrown</i>: <b>general exception log</b>. @returns{dict} response; fields: @dictkey{status,string} 'ok', if succeeded @dictkey{data,dict} response data """ #=========================================================================== # AUTORIZATION #=========================================================================== name = '%s.%s' % (fun.__module__.replace('clm.views.', ''), fun.__name__) request = args[0] data = json.loads(request.body) #=========================================================================== # LOG AGRUMENTS #=========================================================================== gen_exception = False with transaction.commit_manually(): try: # Execute function user_id = auth(is_user, is_clm_superuser, data) resp = fun(**data) if pack_resp and not hasattr(fun, 'packed'): # if function is decorated by cm_request, 'packed' atribbute will be set - response is already packed by cm resp = response('ok', resp) transaction.commit() except CLMException, e: transaction.rollback() user_id = 0 resp = e.response except Exception, e: transaction.rollback() gen_exception = True user_id = 0 resp = response('clm_error', str(e))
def user_update(event, context, user=None, admin=None): item_id = event['pathParameters']['user_id'] item, err = safe_json(event['body']) if err: return item if USER.update(item_id, item): return response(200, {'result': 'Successfully updated'})
def wrapper(request, *args, **kwargs): data = request.GET.dict() data['remote_ip'] = request.META.get('REMOTE_ADDR') gen_exception = False log_enabled = kw.get('log', False) name = '%s.%s' % (fun.__module__.replace('cm.views.', ''), fun.__name__) if log_enabled: log.debug(0, '=' * 100) log.debug(0, 'Function: %s' % name) log.debug(0, 'Args:\n%s' % json.dumps(data, indent=4)) with transaction.commit_manually(): try: # Execute function resp = fun(**data) transaction.commit() except CMException, e: transaction.rollback() log.exception(0, 'CMException %s' % e) resp = e.response except Exception, e: transaction.rollback() gen_exception = True resp = response('cm_error', str(e))
def hello(vm_ip, **args): """ First function which must be called by VMs ctx module. It registers VM with status 'running ctx', also serves a special role when creating farms (tracking head, and worker nodes) @parameter{vm_ip,string} @parameter{args} """ vm = VM.get_by_ip(vm_ip) log.debug(vm.user_id, "Hello from vm %d ip: %s" % (vm.id, vm_ip)) vm.ctx_api_version = args.get('version', None) vm.state = vm_states['running ctx'] if vm.ssh_username and vm.ssh_key: Command.execute('add_ssh_key', vm.user_id, vm.id, user=vm.ssh_username, ssh_key=vm.ssh_key) if vm.is_head(): Command.register_head(vm) elif vm.is_farm(): Command.register_node(vm) try: vm.save(update_fields=['state', 'ctx_api_version']) except Exception, e: log.error( vm.user_id, "Cannot update database for vm %d: %s" % (vm.id, e.message)) return response('ctx_error', "Cannot update database: %s" % e.message)
def hello(vm_ip, **args): """ First function which must be called by VMs ctx module. It registers VM with status 'running ctx', also serves a special role when creating farms (tracking head, and worker nodes) @parameter{vm_ip,string} @parameter{args} """ vm = VM.get_by_ip(vm_ip) log.debug(vm.user_id, "Hello from vm %d ip: %s" % (vm.id, vm_ip)) vm.ctx_api_version = args.get('version', None) vm.state = vm_states['running ctx'] if vm.ssh_username and vm.ssh_key: Command.execute('add_ssh_key', vm.user_id, vm.id, user=vm.ssh_username, ssh_key=vm.ssh_key) if vm.is_head(): Command.register_head(vm) elif vm.is_farm(): Command.register_node(vm) try: vm.save(update_fields=['state', 'ctx_api_version']) except Exception, e: log.error(vm.user_id, "Cannot update database for vm %d: %s" % (vm.id, e.message)) return response('ctx_error', "Cannot update database: %s" % e.message)
def offer_create_with_user(event, context): item, err = safe_json(event['body']) if err: return item result, is_err = OFFERS.create_with_user(item) if is_err: return result return response(200, result)
def validate_for_create(self, item): """ If item is valid, return it with is_error=False If there's an error, return the error response (as dict) along with is_error=True :param item: item to be validated :return: tuple of (object, is_error) """ if self.partition_key in item: return response(400, 'Cannot supply item_id in request'), True return item, False
def validate_for_create(self, item): """ :return: tuple of (response, is_err) If response is an error, return it with is_err=True. If no error, response is the user_item, which can be a) if user already exists, user_item = {'user_id': <user_id>} b) if user doesn't exist, returns dict of user fields to create a new user """ user_id = item.get('user_id', None) if user_id: # Supplied user_id -> look up values and return existing user result = self.get_by_id(user_id) if result: return result, False else: return response(404, f"User not found: {user_id}"), True # call base method item, is_err = super().validate_for_create(item) if is_err: return item first_name = item.get('first_name', None) last_name = item.get('last_name', None) user_id, is_err = self.lookup_by_name(first_name, last_name) if is_err: return user_id, True # error while trying to look up user by name if user_id: return {'user_id': user_id}, False # found existing user # will need to create a new user result = {'create_new_user': True} required_user_fields = [ 'first_name', 'last_name', 'parent_name', 'parent_phone', 'parent_email' ] for field in required_user_fields: if field not in item: return response(400, f'Missing required field: "{field}"'), True else: result[field] = item[field] return result, False
def order_create(event, context): """ Event is a POST from Paypal's IPN (instant payment notification) callback containing all the details of a completed Paypal offer. """ item = {i[0]: i[1] for i in parse_qsl(event['body'])} confirmation = get_paypal_confirmation(item) item['confirmation'] = confirmation if 'item_number' in item: # 'item_number' field contains offer_id. If present, insert it as a nested object offer_id = item['item_number'] offer = OFFERS.get_by_id(offer_id) if offer: item['offer'] = offer new_key = ORDERS.create(item) return response(200, {'order_id': new_key})
def create_with_user(self, item): """ Like POST /offer, but also has user info in the event body :param item: form data with key/values for the offer and for the user :return: Returns tuple of (item, is_err): if result is an error, returns (err_response, True) if result is normal, returns (result, False) """ offer_item, err = self.validate_offer(item) if err: return offer_item, True user_item, is_err = USER.validate_for_create(item) if is_err: # if error, return it return user_item, True # user_item is either a reference to an existing user or a spec for a new user # only return a login_code if a new user has been created # user is the newly created user user = None if user_item.get('create_new_user', False): del user_item['create_new_user'] user_id = USER.create(user_item) user = USER.get_by_id(user_id) login_code = user['login_code'] user_item['login_code'] = login_code else: user_id = user_item.get('user_id', None) login_code = None if not user_id: return response(400, "Missing user_id"), True if not user: # if user is not freshly created, fetch their details user = USER.get_by_id(user_id) # populate offer with some user info offer_item['user_first_name'] = user['first_name'] offer_item['user_last_name'] = user['last_name'] offer_item['user_id'] = user_id offer_id = self.create(offer_item) result = {'user_id': user_id, 'offer_id': offer_id} # only return a login_code if a new user has been created if login_code: result['login_code'] = login_code self.send_confirmation_email(offer_item, user_item) return result, False
def hello(remote_ip, **kw): """ REST stub for hello function @parameter{kw} @returns HTTP response """ vm = VM.get_by_ip(remote_ip) log.info(vm.user_id, "vm called hello") Command.hello(remote_ip) r = response('ok') if int(kw.get('version', 0)) < VERSION: f = file(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'actions.py'), 'r') r['actions_file'] = f.read() f.close() return r
def hello(remote_ip, **kw): """ REST stub for hello function @param_post{remote_ip,string} @param_post{kw} @returns HTTP response """ vm = VM.get_by_ip(remote_ip) log.info(vm.user_id, "vm called hello") Command.hello(remote_ip) r = response('ok') if int(kw.get('version', 0)) < VERSION: f = file(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'actions.py'), 'r') r['actions_file'] = f.read() f.close() return r
def validate_offer(self, item): """ :return: tuple of (response, is_err) If is_err is True, response is an error """ # optional field: 'offer_type_other' result = {'offer_type_other': item.get('offer_type_other', "<empty>")} fields = [ 'offer_type', 'offer_description', 'offer_unit', 'offer_per_hour' ] for field in fields: value = item.get(field, None) if value: result[field] = value else: msg = f'Missing some required fields in offer: missing "{field}"' return response(400, msg), True return result, False
def wrapper(*args, **kwargs): args = list(args) args[0] = process_request(args[0]) try: data = func(*args, **kwargs) if isinstance(data, HttpResponse): return data vars = { 'status':'ok', 'response': data } except ApiException as e: vars = { 'status':'error', 'error': e.message } return process_response(args[0], response(vars))
def finish_command(remote_ip, command_id, status, returns=None, **kw): """ REST stub for finish_command @param_post{remote_ip,string} @param_post{command_id,string} hash string identyfing command @param_post{status,string} @param_post{returns,dict} dictionary containing VM returned values @param_post{kw,dict} keyword params """ vm = VM.get_by_ip(remote_ip) if returns: returns = json.dumps(returns) log.debug(0, "Select command %s %s" % (command_id, status)) try: command = vm.command_set.get(id=command_id) except Command.DoesNotExist: return log.debug(0, "Finish command %s" % command) if command is None: for c in Command.objects.all(): log.debug(0, 'Finish - Available cmds id:%s, state:%s, name:%s, vmid:%s' % (c.id, c.state, c.name, c.vm_id)) return log.debug(vm.user_id, "command state %s" % command.state) command.response = returns command.state = command_states[status] log.debug(vm.user_id, "Finish command %s" % command.id) command.save() r = response('ok') if int(kw.get('version', 0)) < VERSION: f = file(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'actions.py'), 'r') r['actions_file'] = f.read() f.close() return r
def offer_get_all(event, context, admin=None): items = OFFERS.get_all() # populate if missing names for item in items: user_id = item.get('user_id', None) user_first_name = item.get('user_first_name', None) user_last_name = item.get('user_last_name', None) if user_first_name and user_last_name and not admin: redact_name(item) continue if not user_id: # offer has no user_id, give up continue # need to look up user to get the info user = USER.get_by_id(user_id) if not user: # no user found, give up continue item['user_first_name'] = user['first_name'] item['user_last_name'] = user['last_name'] if not admin: redact_name(item) return response(200, {'result': items})
def response(self): """ @returns{dict} common.response() """ return response(str(self))
def response(self): """ @returns{dict} common.response() with the same status as this stringified exception """ return response(str(self))
def order_get_all(event, context, admin=None, user=None): if not admin and not user: return response(401, "Unauthorized") items = ORDERS.get_all() return response(200, {'result': items})
def genericlog(log_enabled, is_user, is_admin_cm, need_ip, fun, args): """ Generic log is called by actor decorators defined in src.clm.utils.decorators : - src.cm.utils.decorators.guest_log - src.cm.utils.decorators.user_log - src.cm.utils.decorators.admin_cm_log It calls decorated functions, additionally performing several tasks. Genericlog performes: -# <i>if decorated function requires user or admin privilidges</i>: <b>authorization</b>; -# <b>execution</b> of the decorated function; -# <i>if \c log_enabled=TRUE or if return status isn't 'ok'</i>: <b>debug log</b> of the \c user_id, function name and arguments; -# <i>if exception is thrown</i>: <b>general exception log</b>; -# <i>if return status isn't 'ok' or \c log_enabled:</i> <b>debug log</b> of the response. @returns{dict} HttpResponse response with content of JSON-ed tuple (status, data), where status should be "ok" if everything went fine. """ #=========================================================================== # AUTORIZATION #=========================================================================== name = '%s.%s' % (fun.__module__.replace('cm.views.', ''), fun.__name__) request = args[0] data = json.loads(request.body) lock_name = None print data if is_user: if len(args) < 1: return response('cm_error', "missing arguments") caller_id = data['caller_id'] if name in ('user.vm.create', 'user.farm.create', 'admin_cm.vm.create', 'admin_cm.farm.create'): lock_name = 'vmcreate' log.debug(caller_id, 'Try acquire lock vmcreate') locks[lock_name].acquire() log.debug(caller_id, 'Lock vmcreate acquired') if is_admin_cm: cm_password = data.pop('cm_password') try: Admin.check_password(caller_id, cm_password) except Exception: return HttpResponse( json.dumps(response('user_permission'), default=json_convert)) else: caller_id = 0 if need_ip: data['remote_ip'] = request.META.get('REMOTE_ADDR') #=========================================================================== # LOG AGRUMENTS #=========================================================================== gen_exception = False if log_enabled: log.debug(caller_id, '=' * 100) log.debug(caller_id, 'Function: %s' % name) log.debug(caller_id, 'Args:\n%s' % json.dumps(data, indent=4)) with transaction.commit_manually(): try: # Execute function resp = response('ok', fun(**data)) transaction.commit() except CMException, e: transaction.rollback() log.exception(caller_id, 'CMException %s' % e) resp = e.response except Exception, e: transaction.rollback() gen_exception = True resp = response('cm_error', str(e))
def user_create(event, context): item, err = safe_json(event['body']) if err: return item new_key = USER.create(item) return response(200, {'user_id': new_key})
def genericlog(log_enabled, is_user, is_admin_cm, need_ip, fun, args): """ Generic log is called by actor decorators defined in src.clm.utils.decorators : - src.cm.utils.decorators.guest_log - src.cm.utils.decorators.user_log - src.cm.utils.decorators.admin_cm_log It calls decorated functions, additionally performing several tasks. Genericlog performes: -# <i>if decorated function requires user or admin privilidges</i>: <b>authorization</b>; -# <b>execution</b> of the decorated function; -# <i>if \c log_enabled=TRUE or if return status isn't 'ok'</i>: <b>debug log</b> of the \c user_id, function name and arguments; -# <i>if exception is thrown</i>: <b>general exception log</b>; -# <i>if return status isn't 'ok' or \c log_enabled:</i> <b>debug log</b> of the response. @returns{dict} HttpResponse response with content of JSON-ed tuple (status, data), where status should be "ok" if everything went fine. """ #=========================================================================== # AUTORIZATION #=========================================================================== name = '%s.%s' % (fun.__module__.replace('cm.views.', ''), fun.__name__) request = args[0] data = json.loads(request.body) lock_name = None print data if is_user: if len(args) < 1: return response('cm_error', "missing arguments") caller_id = data['caller_id'] if name in ('user.vm.create', 'user.farm.create', 'admin_cm.vm.create', 'admin_cm.farm.create'): lock_name = 'vmcreate' log.debug(caller_id, 'Try acquire lock vmcreate') locks[lock_name].acquire() log.debug(caller_id, 'Lock vmcreate acquired') if is_admin_cm: cm_password = data.pop('cm_password') try: Admin.check_password(caller_id, cm_password) except Exception: return HttpResponse(json.dumps(response('user_permission'), default=json_convert)) else: caller_id = 0 if need_ip: data['remote_ip'] = request.META.get('REMOTE_ADDR') #=========================================================================== # LOG AGRUMENTS #=========================================================================== gen_exception = False if log_enabled: log.debug(caller_id, '=' * 100) log.debug(caller_id, 'Function: %s' % name) log.debug(caller_id, 'Args:\n%s' % json.dumps(data, indent=4)) with transaction.commit_manually(): try: # Execute function resp = response('ok', fun(**data)) transaction.commit() except CMException, e: transaction.rollback() log.exception(caller_id, 'CMException %s' % e) resp = e.response except Exception, e: transaction.rollback() gen_exception = True resp = response('cm_error', str(e))
def user_delete(event, context, admin=None): if not admin: return response(401, 'Unauthorized') item_id = event['pathParameters']['user_id'] success = USER.delete(item_id) return response(200, {'success': success})
def user_get_all(event, context, admin=None): if not admin: return response(401, 'Unauthorized') items = USER.get_all() return response(200, {'result': items})