def delete_dns_record(request): """ Delete a specific DNS record under a zone. --- """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict['cloud'] zone_id = request.matchdict['zone'] record_id = request.matchdict['record'] try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id) except me.DoesNotExist: raise CloudNotFoundError try: zone = Zone.objects.get(owner=auth_context.owner, id=zone_id) except Zone.DoesNotExist: raise NotFoundError('Zone does not exist') try: record = Record.objects.get(zone=zone, id=record_id) except Record.DoesNotExist: raise NotFoundError('Record does not exist') auth_context.check_perm("record", "remove", record_id) record.ctl.delete_record() # Schedule a UI update trigger_session_update(auth_context.owner, ['zones']) return OK
def transfer_ownership_to_user(request): """ Tags: ownership --- Transfer ownership of a resource If a resource isn't owned by the requesting user, then an UnauthorizedError error will be thrown, unless the requesting user is a member of the Owners team. """ auth_context = auth_context_from_request(request) params = params_from_request(request) if not params.get('user_id'): raise RequiredParameterMissingError('user_id') try: new_owner = User.objects.get(id=params['user_id']) except User.DoesNotExist: raise NotFoundError('User with id %s' % params['user_id']) for rtype, rids in params.get('resources', {}).iteritems(): Model = get_resource_model(rtype) for rid in rids: try: resource = Model.objects.get(owner=auth_context.owner, id=rid) resource.transfer_ownership(auth_context, new_owner) except Model.DoesNotExist: raise NotFoundError('%s with id %s' % (rtype, rid)) trigger_session_update(auth_context.owner) return Response('OK', 200)
def disassociate_key(request): """ Disassociate a key from a machine Disassociates a key from a machine. If host is set it will also attempt to actually remove it from the machine. READ permission required on cloud. DISASSOCIATE_KEY permission required on machine. --- key: in: path required: true type: string machine: in: path required: true type: string """ key_id = request.matchdict['key'] cloud_id = request.matchdict.get('cloud') auth_context = auth_context_from_request(request) if cloud_id: # this is depracated, keep it for backwards compatibility machine_id = request.matchdict['machine'] try: Cloud.objects.get(owner=auth_context.owner, id=cloud_id, deleted=None) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') auth_context.check_perm("cloud", "read", cloud_id) try: machine = Machine.objects.get(cloud=cloud_id, machine_id=machine_id, state__ne='terminated') except Machine.DoesNotExist: raise NotFoundError("Machine %s doesn't exist" % machine_id) else: machine_uuid = request.matchdict['machine'] try: machine = Machine.objects.get(id=machine_uuid, state__ne='terminated') except Machine.DoesNotExist: raise NotFoundError("Machine %s doesn't exist" % machine_uuid) cloud_id = machine.cloud.id auth_context.check_perm("cloud", "read", cloud_id) auth_context.check_perm("machine", "disassociate_key", machine.id) key = Key.objects.get(owner=auth_context.owner, id=key_id, deleted=None) key.ctl.disassociate(machine) clouds = Cloud.objects(owner=auth_context.owner, deleted=None) machines = Machine.objects(cloud__in=clouds, key_associations__keypair__exact=key) assoc_machines = transform_key_machine_associations(machines, key) return assoc_machines
def associate_key(request): """ Tags: keys --- Associates a key with a machine. If host is set it will also attempt to actually deploy it to the machine. To do that it requires another key (existing_key) that can connect to the machine. READ permission required on cloud. READ_PRIVATE permission required on key. ASSOCIATE_KEY permission required on machine. --- machine: in: path required: true type: string key: in: path required: true type: string port: default: 22 type: integer user: description: The ssh user type: string """ key_id = request.matchdict['key'] params = params_from_request(request) ssh_user = params.get('user', None) try: ssh_port = int(request.json_body.get('port', 22)) except: ssh_port = 22 auth_context = auth_context_from_request(request) try: key = Key.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except Key.DoesNotExist: raise NotFoundError('Key id does not exist') auth_context.check_perm('key', 'read_private', key.id) machine_uuid = request.matchdict['machine_uuid'] try: machine = Machine.objects.get(id=machine_uuid, state__ne='terminated') # used by logging_view_decorator request.environ['machine_id'] = machine.machine_id request.environ['cloud_id'] = machine.cloud.id except Machine.DoesNotExist: raise NotFoundError("Machine %s doesn't exist" % machine_uuid) cloud_id = machine.cloud.id auth_context.check_perm("cloud", "read", cloud_id) auth_context.check_perm("machine", "associate_key", machine.id) return key.ctl.associate(machine, username=ssh_user, port=ssh_port)
def delete_keys(request): """ Tags: keys --- Deletes multiple keys. Provide a list of key ids to be deleted. The method will try to delete all of them and then return a json that describes for each key id whether or not it was deleted or not_found if the key id could not be located. If no key id was found then a 404(Not Found) response will be returned. REMOVE permission required on each key. --- key_ids: required: true type: array items: type: string """ auth_context = auth_context_from_request(request) params = params_from_request(request) key_ids = params.get('key_ids', []) if type(key_ids) != list or len(key_ids) == 0: raise RequiredParameterMissingError('No key ids provided') # remove duplicate ids if there are any key_ids = set(key_ids) report = {} for key_id in key_ids: try: key = Key.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except me.DoesNotExist: report[key_id] = 'not_found' continue try: auth_context.check_perm('key', 'remove', key.id) except PolicyUnauthorizedError: report[key_id] = 'unauthorized' else: delete_key(auth_context.owner, key_id) report[key_id] = 'deleted' # if no key id was valid raise exception if len(filter(lambda key_id: report[key_id] == 'not_found', report)) == len(key_ids): raise NotFoundError('No valid key id provided') # if user was unauthorized for all keys if len(filter(lambda key_id: report[key_id] == 'unauthorized', report)) == len(key_ids): raise NotFoundError('Unauthorized to modify any of the keys') return report
def disassociate_metric(machine, metric_id): """Disassociate a metric from a machine.""" if not machine.monitoring.hasmonitoring: raise MethodNotAllowedError("Machine doesn't have monitoring enabled") try: Metric.objects.get(owner=machine.owner, metric_id=metric_id) except Metric.DoesNotExist: raise NotFoundError("Invalid metric_id") if metric_id not in machine.monitoring.metrics: raise NotFoundError("Metric isn't associated with this Machine") machine.monitoring.metrics.remove(metric_id) machine.save() trigger_session_update(machine.owner, ['monitoring'])
def list_cloud_machines(request): """ Tags: machines --- Lists machines on cloud along with their metadata. Check Permissions takes place in filter_list_machines. READ permission required on cloud. READ permission required on machine. --- cloud: in: path required: true type: string """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict['cloud'] params = params_from_request(request) cached = bool(params.get('cached', False)) # SEC get filtered resources based on auth_context try: Cloud.objects.get(owner=auth_context.owner, id=cloud_id, deleted=None) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') machines = methods.filter_list_machines(auth_context, cloud_id, cached=cached) return machines
def list_folders(request): """ Tags: folders --- Lists all the folders that contain VMs. It is needed for machine creation for the 6.7 REST api of VSphere. In the future it might not be necessary. READ permission required on cloud. --- cloud: in: path required: true type: string """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict.get('cloud') try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id, deleted=None) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') if cloud.as_dict()['provider'] != 'vsphere': raise BadRequestError('Only available for vSphere clouds') # SEC auth_context.check_perm('cloud', 'read', cloud_id) vm_folders = cloud.ctl.compute.list_vm_folders() return vm_folders
def set_key_tags(request): """ Set tags to owner's key EDIT_TAGS permission required on KEY --- key_id: in: path required: true type: string tags: type: dict required: true """ auth_context = auth_context_from_request(request) params = params_from_request(request) key_id = request.matchdict["key_id"] # SEC require EDIT_TAGS permission on key auth_context.check_perm("key", "edit_tags", key_id) try: key = Key.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except Key.DoesNotExist: raise NotFoundError('Resource with that id does not exist') tags = params.get("tags") if type(tags) != dict: raise BadRequestError('tags should be dictionary of tags') if not modify_security_tags(auth_context, tags, key): raise auth_context._raise('key', 'edit_security_tags') return add_tags_to_resource(auth_context.owner, key, tags.items())
def delete_key(request): """ Delete key Delete key. When a key gets deleted, it takes its associations with it so just need to remove from the server too. If the default key gets deleted, it sets the next one as default, provided that at least another key exists. It returns the list of all keys after the deletion, excluding the private keys (check also list_keys). REMOVE permission required on key. --- key: in: path required: true type: string """ auth_context = auth_context_from_request(request) key_id = request.matchdict.get('key') if not key_id: raise KeyParameterMissingError() try: key = Key.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Key id does not exist') auth_context.check_perm('key', 'remove', key.id) m_delete_key(auth_context.owner, key_id) return list_keys(request)
def get_private_key(request): """ Gets private key from key name. It is used in single key view when the user clicks the display private key button. READ_PRIVATE permission required on key. --- key: description: The key id in: path required: true type: string """ key_id = request.matchdict['key'] if not key_id: raise RequiredParameterMissingError("key_id") auth_context = auth_context_from_request(request) try: key = SSHKey.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Key id does not exist') auth_context.check_perm('key', 'read_private', key.id) return key.private
def rename_cloud(request): """ Rename a cloud Renames cloud with given cloud_id. EDIT permission required on cloud. --- cloud: in: path required: true type: string new_name: description: ' New name for the key (will also serve as the key''s id)' type: string """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict['cloud'] try: Cloud.objects.get(owner=auth_context.owner, id=cloud_id, deleted=None) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') params = params_from_request(request) new_name = params.get('new_name', '') if not new_name: raise RequiredParameterMissingError('new_name') auth_context.check_perm('cloud', 'edit', cloud_id) m_rename_cloud(auth_context.owner, cloud_id, new_name) return OK
def list_dns_records(request): """ Tags: dns --- Lists all DNS records for a particular zone. READ permission required on zone and record. --- cloud: in: path required: true type: string zone: in: path required: true type: string """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict['cloud'] zone_id = request.matchdict['zone'] try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id) except me.DoesNotExist: raise CloudNotFoundError try: zone = Zone.objects.get(owner=auth_context.owner, cloud=cloud, id=zone_id) except Zone.DoesNotExist: raise NotFoundError('Zone does not exist') return filter_list_records(auth_context, zone)
def _on_response_callback(response, tornado_async=False): """HTTP Response-handling callback. This method is meant to return HTTP Response objects generated either in a Tornado or synchronous execution context. Arguments: - response: HTTP Response object. - tornado_async: Denotes if a Tornado-safe HTTP request was issued. """ if tornado_async: if response.code != 200: log.error( 'Error on Elasticsearch query in tornado_async mode. ' 'Got %d status code: %s', response.code, response.body) if response.code == 400: raise BadRequestError() if response.code == 404: raise NotFoundError() if response.code == 429: raise RateLimitError() raise ServiceUnavailableError() response = json.loads(response.body) return response
def star_image(request): """ Tags: images --- Star/unstar an image. Toggle image star (star/unstar). EDIT permission required on cloud. --- cloud: in: path required: true type: string image: description: Id of image to be used with the creation in: path required: true type: string """ cloud_id = request.matchdict['cloud'] image_id = request.matchdict['image'] auth_context = auth_context_from_request(request) auth_context.check_perm("cloud", "edit", cloud_id) try: Cloud.objects.get(owner=auth_context.owner, id=cloud_id) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') return methods.star_image(auth_context.owner, cloud_id, image_id)
def search_image(request): """ Tags: images --- Search images from cloud. If a search_term is provided, we search for that term in the ids and the names of the community images. Available for EC2 and Docker. READ permission required on cloud. --- cloud: in: path required: true type: string search_term: type: string """ cloud_id = request.matchdict['cloud'] auth_context = auth_context_from_request(request) try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') if cloud.ctl.provider not in ['ec2', 'docker']: raise NotImplementedError( "Search images only supported for EC2 and Docker") return list_images(request)
def list_datastores(request): """ Tags: datastores --- Lists datastores on cloud. Only supported for Vsphere. READ permission required on cloud. --- cloud: in: path required: true type: string """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict.get('cloud') try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id, deleted=None) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') if cloud.as_dict()['provider'] != 'vsphere': raise BadRequestError('Only available for vSphere clouds.') # SEC auth_context.check_perm('cloud', 'read', cloud_id) try: datastores = cloud.ctl.compute.list_datastores() return datastores except Exception as e: log.error("Could not list datastores for cloud %s: %r" % (cloud, e)) raise MistNotImplementedError()
def edit_key(request): """ Edit a key Edits a given key's name to new_name EDIT permission required on key. --- new_name: description: The new key name type: string key_id: description: The key id in: path required: true type: string """ key_id = request.matchdict['key'] params = params_from_request(request) new_name = params.get('new_name') if not new_name: raise RequiredParameterMissingError("new_name") auth_context = auth_context_from_request(request) try: key = Key.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Key with that id does not exist') auth_context.check_perm('key', 'edit', key.id) key.ctl.rename(new_name) return {'new_name': new_name}
def set_default_key(request): """ Set default key Sets a new default key EDIT permission required on key. --- key: description: The key id in: path required: true type: string """ key_id = request.matchdict['key'] auth_context = auth_context_from_request(request) try: key = Key.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Key id does not exist') auth_context.check_perm('key', 'edit', key.id) key.ctl.set_default() return OK
def show_script(request): """ Show script details and job history. READ permission required on script. --- script_id: type: string required: true in: path """ script_id = request.matchdict['script_id'] auth_context = auth_context_from_request(request) if not script_id: raise RequiredParameterMissingError('No script id provided') try: script = Script.objects.get(owner=auth_context.owner, id=script_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Script id not found') # SEC require READ permission on SCRIPT auth_context.check_perm('script', 'read', script_id) ret_dict = script.as_dict() jobs = get_stories('job', auth_context.owner.id, script_id=script_id) ret_dict['jobs'] = [job['job_id'] for job in jobs] return ret_dict
def get_public_key(request): """ Get public key Gets public key from key name. READ permission required on key. --- key: description: The key id in: path required: true type: string """ key_id = request.matchdict['key'] if not key_id: raise RequiredParameterMissingError("key_id") auth_context = auth_context_from_request(request) try: key = SSHKey.objects.get(owner=auth_context.owner, id=key_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Key id does not exist') auth_context.check_perm('key', 'read', key.id) return key.public
def download_script(request): """ Download script file or archive. READ permission required on script. --- script_id: type: string required: true in: path """ script_id = request.matchdict['script_id'] auth_context = auth_context_from_request(request) if not script_id: raise RequiredParameterMissingError('No script id provided') try: script = Script.objects.get(owner=auth_context.owner, id=script_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Script id not found') # SEC require READ permission on SCRIPT auth_context.check_perm('script', 'read', script_id) try: return script.ctl.get_file() except BadRequestError(): return Response("Unable to find: {}".format(request.path_info))
def set_machine_tags(request): """ Set tags on a machine Set tags for a machine, given the cloud and machine id. READ permission required on cloud. EDIT_TAGS permission required on machine. --- cloud_id: in: path required: true type: string machine_id: in: path required: true type: string tags: items: type: object type: array """ auth_context = auth_context_from_request(request) params = params_from_request(request) cloud_id = request.matchdict["cloud_id"] machine_id = request.matchdict["machine_id"] auth_context.check_perm("cloud", "read", cloud_id) try: machine = Machine.objects.get(cloud=cloud_id, machine_id=machine_id) except me.DoesNotExist: raise NotFoundError('Resource with that id does not exist') # SEC require EDIT_TAGS permission on machine auth_context.check_perm("machine", "edit_tags", machine.id) tags = params.get("tags") if type(tags) != dict: raise BadRequestError('tags should be dictionary of tags') if not modify_security_tags(auth_context, tags, machine): raise auth_context._raise('machine', 'edit_security_tags') # FIXME: This is f***** up! This method is utilized by the Ember UI in # order to update a machine's tags by providing the entire list of tags # to be re-set. However, `add_tags_to_resource` simply appends the new # tags without deleting any. old_tags = get_tags_for_resource(auth_context.owner, machine) add_tags_to_resource(auth_context.owner, machine, tags.items()) if config.MACHINE_PATCHES: new_tags = get_tags_for_resource(auth_context.owner, machine) patch = jsonpatch.JsonPatch.from_diff(old_tags, new_tags).patch for item in patch: item['path'] = '/%s-%s/tags%s' % (machine.id, machine.machine_id, item['path']) amqp_publish_user(auth_context.owner.id, routing_key='patch_machines', data={'cloud_id': cloud_id, 'patch': patch}) return {}
def set_cloud_tags(request): """ Tags: tags --- Set tags to owner's cloud. EDIT_TAGS permission required on SCRIPT --- tags: type: dict required: true """ auth_context = auth_context_from_request(request) params = params_from_request(request) cloud_id = request.matchdict["cloud_id"] # SEC require EDIT_TAGS permission on cloud auth_context.check_perm("cloud", "edit_tags", cloud_id) try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Resource with that id does not exist') tags = params.get("tags") if type(tags) != dict: raise BadRequestError('tags should be dictionary of tags') if not modify_security_tags(auth_context, tags, cloud): raise auth_context._raise('cloud', 'edit_security_tags') return add_tags_to_resource(auth_context.owner, cloud, list(tags.items()))
def delete_script(request): """ Delete script REMOVE permission required on script. --- script_id: in: path required: true type: string """ script_id = request.matchdict['script_id'] auth_context = auth_context_from_request(request) if not script_id: raise RequiredParameterMissingError('No script id provided') try: script = Script.objects.get(owner=auth_context.owner, id=script_id, deleted=None) except me.DoesNotExist: raise NotFoundError('Script id not found') # SEC require REMOVE permission on script auth_context.check_perm('script', 'remove', script_id) script.ctl.delete() return OK
def list_projects(request): """ Tags: projects --- Lists projects on cloud. Only supported for EquinixMetal. For other providers,returns an empty list READ permission required on cloud. --- cloud: in: path required: true type: string """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict['cloud'] # SEC auth_context.check_perm("cloud", "read", cloud_id) try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id, deleted=None) except Cloud.DoesNotExist: raise NotFoundError('Cloud does not exist') try: projects = methods.list_projects(auth_context.owner, cloud_id) except Exception as e: log.error("Could not list projects for cloud %s: %r" % (cloud, e)) raise MistNotImplementedError() return projects
def delete_dns_zone(request): """ Delete a specific DNS zone under a cloud. --- """ auth_context = auth_context_from_request(request) cloud_id = request.matchdict['cloud'] zone_id = request.matchdict['zone'] # Do we need the cloud here, now that the models have been created? try: cloud = Cloud.objects.get(owner=auth_context.owner, id=cloud_id) except me.DoesNotExist: raise CloudNotFoundError try: zone = Zone.objects.get(owner=auth_context.owner, id=zone_id) except Zone.DoesNotExist: raise NotFoundError('Zone does not exist') auth_context.check_perm("zone", "remove", zone_id) zone.ctl.delete_zone() # Schedule a UI update trigger_session_update(auth_context.owner, ['zones']) return OK
def url_script(request): """ Returns to a mist authenticated user, a self-auth/signed url for fetching a script's file. READ permission required on script. --- script_id: in: path required: true type: string """ auth_context = auth_context_from_request(request) script_id = request.matchdict['script_id'] try: Script.objects.get(owner=auth_context.owner, id=script_id, deleted=None) except Script.DoesNotExist: raise NotFoundError('Script does not exist.') # SEC require READ permission on script auth_context.check_perm('script', 'read', script_id) # build HMAC and inject into the `curl` command hmac_params = {'action': 'fetch_script', 'object_id': script_id} expires_in = 60 * 15 mac_sign(hmac_params, expires_in) url = "%s/api/v1/fetch" % config.CORE_URI encode_params = urllib.urlencode(hmac_params) r_url = url + '?' + encode_params return r_url
def revoke_session(request): """ Tags: sessions --- Revoke an active session --- id: description: Session ID """ auth_context = auth_context_from_request(request) params = params_from_request(request) auth_token_id = params.get("id") if not auth_token_id: raise RequiredParameterMissingError("No token id parameter provided") try: if auth_context.is_owner(): auth_token = AuthToken.objects.get(org=auth_context.org, id=auth_token_id) else: auth_token = AuthToken.objects.get( user_id=auth_context.user.get_id(), id=auth_token_id) if auth_token.is_valid(): auth_token.invalidate() auth_token.save() except me.DoesNotExist: raise NotFoundError('Session not found') return OK
def fetch_script(script_id): """Used by mist.api.views.fetch""" try: script = Script.objects.get(id=script_id, deleted=None) except Script.DoesNotExist: raise NotFoundError('Script does not exist') return script.ctl.get_file()