def delete_account(request): """ A user requests his account to be deleted. In order to verify the request the user's password should be included as a parameter. """ email = request.matchdict['email'] params = params_from_request(request) plaintext_password = params.get('password', None) if not email: raise ValueError('No email provided') if config.SSO_TEST_EMAIL == "": raise MethodNotAllowedError("Configuration error") # SEC only allow test user to self delete if email != config.SSO_TEST_EMAIL: raise MethodNotAllowedError("This method is only for the test user") try: user = User.objects.get(email=email) except UserNotFoundError: return OK if not user.check_password(password=plaintext_password): raise MethodNotAllowedError("Password is wrong!!") user.delete() return OK
def register_user(email, first_name, last_name, registration_method, selected_plan=None, promo_code=None, token=None, status='pending', create_organization=True, request=None): # User does not exist so we have to add him/her to the database # First make sure that email is not banned # Then create the User objects and the Organization if email.split('@')[1] in config.BANNED_EMAIL_PROVIDERS: raise MethodNotAllowedError("Email provider is banned.") user = User() user.email = email user.first_name = first_name user.last_name = last_name user.registration_method = registration_method user.registration_date = time() user.status = status user.activation_key = get_secure_rand_token() user.can_create_org = True user.save() # For some users registering through sso it might not be necessary to # create an organization, hence the flag org = create_org_for_user(user, '', promo_code, token, selected_plan) \ if create_organization else None log_event_args = { 'owner_id': org and org.id or '', 'user_id': user.id, 'first_name': user.first_name, 'last_name': user.last_name, 'company': user.feedback.company_name, 'event_type': 'request', 'action': 'register', 'authentication_provider': registration_method } if request: log_event_args.update({ 'request_method': request.method, 'request_path': request.path, 'request_ip': ip_from_request(request), 'user_agent': request.user_agent, }) if org: log_event_args.update({'org_id': org.id, 'org_name': org.name}) # Create log for the registration of a user and if an org has been created # add the id and name of the org from mist.api.logs.methods import log_event log_event(**log_event_args) return user, org
def get_stats(request): """ Tags: monitoring --- Request monitoring data for a machine --- machine: in: path type: string required: true start: in: query type: string default: now required: false description: time (eg. '10s') since when to fetch stats stop: in: query type: string required: false description: time until when to fetch stats step: in: query type: string required: false description: step to fetch stats, used in aggregations metrics: in: query type: string required: false request_id: in: query type: string required: false """ machine = _machine_from_matchdict(request) if not machine.monitoring.hasmonitoring: raise MethodNotAllowedError("Machine doesn't have monitoring enabled") # SEC require permission READ on machine auth_context = auth_context_from_request(request) auth_context.check_perm('machine', 'read', machine.id) params = params_from_request(request) start = params.get('start', '') stop = params.get('stop', '') step = params.get('step', '') try: metrics = params.getall('metrics') except: metrics = params.get('metrics') data = mist.api.monitoring.methods.get_stats(machine, start=start, stop=stop, step=step, metrics=metrics) data['request_id'] = params.get('request_id') return data
def associate_metric(machine, metric_id, name='', unit=''): """Associate a new metric to a machine.""" if not machine.monitoring.hasmonitoring: raise MethodNotAllowedError("Machine doesn't have monitoring enabled") metric = update_metric(machine.owner, metric_id, name, unit) if metric_id not in machine.monitoring.metrics: machine.monitoring.metrics.append(metric_id) machine.save() trigger_session_update(machine.owner, ['monitoring']) return metric
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 find_metrics(machine): """Return the metrics associated with the specified machine.""" if not machine.monitoring.hasmonitoring: raise MethodNotAllowedError("Machine doesn't have monitoring enabled") if machine.monitoring.method in ('telegraf-graphite'): return graphite_find_metrics(machine) elif machine.monitoring.method == 'telegraf-influxdb': metrics = {} for metric in show_fields(show_measurements(machine.id)): metrics[metric['id']] = metric return metrics else: raise Exception("Invalid monitoring method")
def machine_dashboard(request): """ Tags: monitoring --- Return monitoring dashboard for a machine. READ permission required on cloud. READ permission required on machine. """ machine = _machine_from_matchdict(request) if not machine.monitoring.hasmonitoring: raise MethodNotAllowedError("Machine doesn't have monitoring enabled") if machine.monitoring.method in ('telegraf-graphite'): if machine.os_type == "windows": ret = copy.deepcopy(config.WINDOWS_MACHINE_DASHBOARD_DEFAULT) else: ret = copy.deepcopy(config.GRAPHITE_MACHINE_DASHBOARD_DEFAULT) elif machine.monitoring.method in ('telegraf-tsfdb'): ret = copy.deepcopy(config.FDB_MACHINE_DASHBOARD_DEFAULT) else: ret = copy.deepcopy(config.INFLUXDB_MACHINE_DASHBOARD_DEFAULT) dashboard = ret['dashboard'] for m in machine.monitoring.metrics: panels = dashboard['rows'][-1]['panels'] panels.append({ "id": len(panels), "title": m.replace('mist.python', '').replace('.', ' '), "type": "graph", "span": 6, "stack": False, "removable": True, "datasource": "mist.monitor", "targets": [{ "refId": "m", "target": m }], "x-axis": True, "y-axis": True }) for i in range(0, len(dashboard['rows'])): for j in range(0, len(dashboard['rows'][i]['panels'])): dashboard['rows'][i]['panels'][j]['machine'] = [ machine.cloud.id, machine.machine_id ] return ret
def get_stats(machine, start='', stop='', step='', metrics=None): """Get all monitoring data for the specified machine. If a list of `metrics` is provided, each metric needs to comply with the following template: <measurement>.<tags>.<column> where <tags> (optional) must be in "key=value" format and delimited by ".". Regular expressions may also be specified, but they need to be inside `/`, as defined by InfluxQL. The usage of "." should be avoided when using regular expressions, since dots are also used to delimit the metrics' path. Arguments: - machine: Machine model instance to get stats for - start: the time since when to query for stats, eg. 10s, 2m, etc - stop: the time until which to query for stats - step: the step at which to return stats - metrics: the metrics to query for, if explicitly specified """ if not machine.monitoring.hasmonitoring: raise MethodNotAllowedError('Machine does not have monitoring enabled') if metrics is None: metrics = [] elif not isinstance(metrics, list): metrics = [metrics] if machine.monitoring.method in ('telegraf-graphite'): return graphite_get_stats( machine, start=start, stop=stop, step=step, metrics=metrics, ) elif machine.monitoring.method == 'telegraf-influxdb': if not metrics: metrics = (config.INFLUXDB_BUILTIN_METRICS.keys() + machine.monitoring.metrics) # NOTE: For backwards compatibility. # Transform "min" and "sec" to "m" and "s", respectively. start, stop, step = map(lambda x: re.sub('in|ec', repl='', string=x), (start.strip('-'), stop.strip('-'), step)) # Fetch series. results = {} for metric in metrics: regex = r'^(?:\w+)\((.+)\)$' match = re.match(regex, metric) if not match: groups = (metric, ) while match: groups = match.groups() match = re.match(regex, groups[0]) measurement, _ = groups[0].split('.', 1) handler = INFLUXDB_HANDLERS.get(measurement, InfluxMainStatsHandler)(machine) data = handler.get_stats(metric=metric, start=start, stop=stop, step=step) if data: results.update(data) return results else: raise Exception("Invalid monitoring method")
def machine_console(request): """ Tags: machines --- Open VNC console. Generate and return an URI to open a VNC console to target machine READ permission required on cloud. READ permission required on machine. --- cloud: in: path required: true type: string machine: in: path required: true type: string rdp_port: default: 3389 in: query required: true type: integer host: in: query required: true type: string """ cloud_id = request.matchdict.get('cloud') auth_context = auth_context_from_request(request) if cloud_id: machine_id = request.matchdict['machine'] auth_context.check_perm("cloud", "read", cloud_id) try: machine = Machine.objects.get(cloud=cloud_id, machine_id=machine_id, state__ne='terminated') # used by logging_view_decorator request.environ['machine_uuid'] = machine.id except Machine.DoesNotExist: raise NotFoundError("Machine %s doesn't exist" % machine_id) else: 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", "read", machine.id) if machine.cloud.ctl.provider not in ['vsphere', 'openstack', 'libvirt']: raise MistNotImplementedError( "VNC console only supported for vSphere, OpenStack or KVM") if machine.cloud.ctl.provider == 'libvirt': import xml.etree.ElementTree as ET from html import unescape from datetime import datetime import hmac import hashlib xml_desc = unescape(machine.extra.get('xml_description', '')) root = ET.fromstring(xml_desc) vnc_element = root.find('devices').find('graphics[@type="vnc"]') if not vnc_element: raise MethodNotAllowedError( "VNC console not supported by this KVM domain") vnc_port = vnc_element.attrib.get('port') vnc_host = vnc_element.attrib.get('listen') from mongoengine import Q # Get key associations, prefer root or sudoer ones key_associations = KeyMachineAssociation.objects( Q(machine=machine.parent) & (Q(ssh_user='******') | Q(sudo=True))) \ or KeyMachineAssociation.objects(machine=machine.parent) if not key_associations: raise ForbiddenError() key_id = key_associations[0].key.id host = '%s@%s:%d' % (key_associations[0].ssh_user, machine.parent.hostname, key_associations[0].port) expiry = int(datetime.now().timestamp()) + 100 msg = '%s,%s,%s,%s,%s' % (host, key_id, vnc_host, vnc_port, expiry) mac = hmac.new(config.SECRET.encode(), msg=msg.encode(), digestmod=hashlib.sha256).hexdigest() base_ws_uri = config.CORE_URI.replace('http', 'ws') proxy_uri = '%s/proxy/%s/%s/%s/%s/%s/%s' % ( base_ws_uri, host, key_id, vnc_host, vnc_port, expiry, mac) return render_to_response('../templates/novnc.pt', {'url': proxy_uri}) if machine.cloud.ctl.provider == 'vsphere': console_uri = machine.cloud.ctl.compute.connection.ex_open_console( machine.machine_id) protocol, host = config.CORE_URI.split('://') protocol = protocol.replace('http', 'ws') params = urllib.parse.urlencode({'url': console_uri}) proxy_uri = f"{protocol}://{host}/wsproxy/?{params}" return render_to_response('../templates/novnc.pt', {'url': proxy_uri}) else: console_url = machine.cloud.ctl.compute.connection.ex_open_console( machine.machine_id) raise RedirectError(console_url)