def test_data_values_override_defaults(self): key = factory.make_name("key") defaults = {key: factory.make_name("key")} data_value = factory.make_name("value") data = {key: data_value} results = get_overridden_query_dict(defaults, data, [key]) self.assertEqual([data_value], results.getlist(key))
def test_returns_QueryDict(self): fields = [factory.make_name("field")] defaults = {fields[0]: factory.make_name("field")} results = get_overridden_query_dict(defaults, QueryDict(""), fields) expected_results = QueryDict("").copy() expected_results.update(defaults) self.assertEqual(expected_results, results)
def test_querydict_data_values_override_defaults(self): key = factory.make_name("key") defaults = {key: factory.make_name("name")} data_values = [factory.make_name("value") for _ in range(2)] data = QueryDict("").copy() data.setlist(key, data_values) results = get_overridden_query_dict(defaults, data, [key]) self.assertEqual(data_values, results.getlist(key))
def test_fields_filter_results(self): key1 = factory.make_string() key2 = factory.make_string() defaults = {key1: factory.make_string(), key2: factory.make_string()} data_value1 = factory.make_string() data_value2 = factory.make_string() data = {key1: data_value1, key2: data_value2} results = get_overridden_query_dict(defaults, data, [key1]) self.assertEqual([data_value2], results.getlist(key2))
def create_node( architecture, power_type, power_parameters, mac_addresses, domain=None, hostname=None, ): """Create a new `Node` and return it. :param architecture: The architecture of the new node. :param power_type: The power type of the new node. :param power_parameters: A JSON-encoded string of power parameters for the new node. :param mac_addresses: An iterable of MAC addresses that belong to the node. :param domain: The domain the node should join. :param hostname: the desired hostname for the new node """ # Check that there isn't already a node with one of our MAC # addresses, and bail out early if there is. nodes = Node.objects.filter(interface__mac_address__in=mac_addresses) if nodes.count() > 0: raise NodeAlreadyExists( "One of the MACs %s is already in use by a node." % mac_addresses ) # It is possible that the enlistment code did not provide a subarchitecture # for the give architecture; assume 'generic'. if "/" not in architecture: architecture = "%s/generic" % architecture data = { "power_type": power_type, "power_parameters": power_parameters, "architecture": architecture, "mac_addresses": mac_addresses, } if domain is not None: data["domain"] = domain if hostname is not None: data["hostname"] = hostname.strip() data_query_dict = get_overridden_query_dict( {}, data, AdminMachineWithMACAddressesForm.Meta.fields ) form = AdminMachineWithMACAddressesForm(data_query_dict) if form.is_valid(): node = form.save() return node else: raise ValidationError(form.errors)
def test_expands_dict_fields(self): field_name = factory.make_name("field_name") sub_fields = { factory.make_name("sub_field"): CharField() for _ in range(3) } fields = {field_name: DictCharField(sub_fields)} defaults = { "%s_%s" % (field_name, field): factory.make_name("subfield") for field in sub_fields.keys() } data = {field_name: DictCharField(fields)} results = get_overridden_query_dict(defaults, data, fields) expected = {key: Equals(value) for key, value in defaults.items()} expected.update({ name: IsInstance(value.__class__) for name, value in fields.items() }) self.assertThat(results, MatchesDict(expected))
def query(self, request): """@description-title List node events @description List node events, optionally filtered by various criteria via URL query parameters. @param (string) "hostname" [required=false] An optional hostname. Only events relating to the node with the matching hostname will be returned. This can be specified multiple times to get events relating to more than one node. @param (string) "mac_address" [required=false] An optional list of MAC addresses. Only nodes with matching MAC addresses will be returned. @param (string) "id" [required=false] An optional list of system ids. Only nodes with matching system ids will be returned. @param (string) "zone" [required=false] An optional name for a physical zone. Only nodes in the zone will be returned. @param (string) "agent_name" [required=false] An optional agent name. Only nodes with matching agent names will be returned. @param (string) "level" [required=false] Desired minimum log level of returned events. Returns this level of events and greater. Choose from: %(log_levels)s. The default is INFO. @param (string) "limit" [required=false] Optional number of events to return. Default 100. Maximum: 1000. @param (string) "before" [required=false] Optional event id. Defines where to start returning older events. @param (string) "after" [required=false] Optional event id. Defines where to start returning newer events. @param (string) "owner" [required=false] If specified, filters the list to show only events owned by the specified username. @success (http-status-code) "server-success" 200 @success (json) "success-json" A JSON object containing a list of events objects. @success-example "success-json" [exkey=events-query] placeholder text """ # Extract & validate optional parameters from the request. after = get_optional_param(request.GET, 'after', None, Int) before = get_optional_param(request.GET, 'before', None, Int) level = get_optional_param(request.GET, 'level', 'INFO') limit = get_optional_param( request.GET, "limit", DEFAULT_EVENT_LOG_LIMIT, Int) owner = get_optional_param(request.GET, 'owner', default=None) # Limit what we'll return to avoid being swamped. if limit > MAX_EVENT_LOG_COUNT: raise MAASAPIBadRequest(( "Requested number of events %d is greater than" " limit: %d") % (limit, MAX_EVENT_LOG_COUNT)) else: # The limit should never be less than 1. limit = 1 if limit < 1 else limit # Filter first by optional node ID, hostname, MAC, etc. nodes = filtered_nodes_list_from_request(request) # Event lists aren't supported on devices. nodes = nodes.exclude(node_type=NODE_TYPE.DEVICE) # Check first for AUDIT level. if level == LOGGING_LEVELS[AUDIT]: events = Event.objects.filter(type__level=AUDIT) elif level in LOGGING_LEVELS_BY_NAME: events = Event.objects.filter(node__in=nodes) # Eliminate logs below the requested level. events = events.exclude( type__level__lt=LOGGING_LEVELS_BY_NAME[level]) elif level is not None: raise MAASAPIBadRequest( "Unrecognised log level: %s" % level) events = ( events.all() .select_related('type') .select_related('node')) # Filter events for owner. if owner is not None: events = events.filter(username=owner) # Future feature: # This is where we would filter for events 'since last node deployment' # using a query param like since_last_deployed=true, but we aren't # right now because we don't currently record a timestamp of the last # deployment, and we don't have an event subtype for node status # changes to filter for the deploying status event. if after is None and before is None: # Get `limit` events, newest first. events = events.order_by('-id') events = events[:limit] elif after is None: # Get `limit` events, newest first, all before `before`. events = events.filter(id__lt=before) events = events.order_by('-id') events = events[:limit] elif before is None: # Get `limit` events, OLDEST first, all after `after`, then # reverse the results. events = events.filter(id__gt=after) events = events.order_by('id') events = reversed(events[:limit]) else: raise MAASAPIBadRequest( "There is undetermined behaviour when both " "`after` and `before` are specified.") # We need to load all of these events at some point, so save them # into a list now so that len() is cheap. events = list(events) # Helper for building prev_uri and next_uri. def make_uri(params, base=reverse('events_handler')): query = urllib.parse.urlencode(params, doseq=True) url = urllib.parse.urlparse(base)._replace(query=query) return url.geturl() # Figure out a URI to obtain a set of newer events. next_uri_params = get_overridden_query_dict( request.GET, {"before": []}, self.all_params) if len(events) == 0: if before is None: # There are no newer events NOW, but there may be later. next_uri = make_uri(next_uri_params) else: # Without limiting to `before`, we might find some more events. next_uri_params["after"] = before - 1 next_uri = make_uri(next_uri_params) else: # The first event is the newest. next_uri_params["after"] = str(events[0].id) next_uri = make_uri(next_uri_params) # Figure out a URI to obtain a set of older events. prev_uri_params = get_overridden_query_dict( request.GET, {"after": []}, self.all_params) if len(events) == 0: if after is None: # There are no older events and never will be. prev_uri = None else: # Without limiting to `after`, we might find some more events. prev_uri_params["before"] = after + 1 prev_uri = make_uri(prev_uri_params) else: # The last event is the oldest. prev_uri_params["before"] = str(events[-1].id) prev_uri = make_uri(prev_uri_params) return { "count": len(events), "events": [event_to_dict(event) for event in events], "next_uri": next_uri, "prev_uri": prev_uri, }
def __init__(self, dict, fields, user=None): if user is None: user = factory.make_User() self.user = user self.GET = get_overridden_query_dict(dict, QueryDict(""), fields)
def __init__(self, dict, fields): self.user = factory.make_User() self.GET = get_overridden_query_dict(dict, QueryDict(''), fields)
def test_takes_multiple_values_in_default_parameters(self): values = [factory.make_name("value") for _ in range(2)] key = factory.make_name("key") defaults = {key: values} results = get_overridden_query_dict(defaults, {}, [key]) self.assertEqual(values, results.getlist(key))