Esempio n. 1
0
    def read(self, request, system_id, id):
        """View a specific set of results.

        id can either by the script set id, current-commissioning,
        current-testing, or current-installation.

        :param hardware_type: Only return scripts for the given hardware type.
            Can be node, cpu, memory, or storage. Defaults to all.
        :type script_type: unicode

        :param include_output: Include base64 encoded output from the script.
        :type include_output: bool

        :param filters: A comma seperated list to show only results that ran
                        with a script name, tag, or id.
        :type filters: unicode
        """
        script_set = self._get_script_set(request, system_id, id)
        include_output = get_optional_param(request.GET, 'include_output',
                                            False, Bool)
        filters = get_optional_param(request.GET, 'filters', None, String)
        if filters is not None:
            filters = filters.split(',')
        hardware_type = get_optional_param(request.GET, 'hardware_type')
        if hardware_type is not None:
            try:
                hardware_type = translate_hardware_type(hardware_type)
            except ValidationError as e:
                raise MAASAPIValidationError(e)
        script_set.include_output = include_output
        script_set.filters = filters
        script_set.hardware_type = hardware_type
        return script_set
Esempio n. 2
0
    def statistics(self, request, id):
        """@description-title Get subnet statistics
        @description Returns statistics for the specified subnet, including:

        - **num_available**: the number of available IP addresses
        - **largest_available**: the largest number of contiguous free IP
          addresses
        - **num_unavailable**: the number of unavailable IP addresses
        - **total_addresses**: the sum of the available plus unavailable
          addresses
        - **usage**: the (floating point) usage percentage of this subnet
        - **usage_string**: the (formatted unicode) usage percentage of this
          subnet
        - **ranges**: the specific IP ranges present in ths subnet (if
          specified)

        Note: to supply additional optional parameters for this request, add
        them to the request URI: e.g.
        ``/subnets/1/?op=statistics&include_suggestions=1``

        @param (int) "{id}" [required=true] A subnet ID.

        @param (int) "include_ranges" [required=false] If '1', includes
        detailed information about the usage of this range. '1' == True, '0' ==
        False.

        @param (int) "include_suggestions" [required=false] If '1', includes
        the suggested gateway and dynamic range for this subnet, if it were to
        be configured. '1' == True, '0' == False.

        @success (http-status-code) "server-success" 200
        @success (json) "success-json" A JSON object containing the statistics.
        @success-example "success-json" [exkey=subnets-statistics]
        placeholder text

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested subnet is not found.
        @error-example "not-found"
            Not Found
        """
        subnet = Subnet.objects.get_subnet_or_404(
            id, request.user, NodePermission.view
        )
        include_ranges = get_optional_param(
            request.GET, "include_ranges", default=False, validator=StringBool
        )
        include_suggestions = get_optional_param(
            request.GET,
            "include_suggestions",
            default=False,
            validator=StringBool,
        )
        full_iprange = subnet.get_iprange_usage()
        statistics = IPRangeStatistics(full_iprange)
        return statistics.render_json(
            include_ranges=include_ranges,
            include_suggestions=include_suggestions,
        )
Esempio n. 3
0
    def read(self, request):
        """Return a list of stored scripts.

        :param type: Only return scripts with the given type. This can be
            testing or commissioning. Defaults to showing both.
        :type type: unicode

        :param hardware_type: Only return scripts for the given hardware type.
            Can be node, cpu, memory, or storage. Defaults to all.
        :type hardware_type: unicode

        :param include_script: Include the base64 encoded script content.
        :type include_script: bool

        :param filters: A comma seperated list to show only results
                        with a script name or tag.
        :type filters: unicode
        """
        qs = Script.objects.all()

        script_type = get_optional_param(request.GET, 'type')
        if script_type is not None:
            try:
                script_type = translate_script_type(script_type)
            except ValidationError as e:
                raise MAASAPIValidationError(e)
            else:
                qs = qs.filter(script_type=script_type)

        hardware_type = get_optional_param(request.GET, 'hardware_type')
        if hardware_type is not None:
            try:
                hardware_type = translate_hardware_type(hardware_type)
            except ValidationError as e:
                raise MAASAPIValidationError(e)
            else:
                qs = qs.filter(hardware_type=hardware_type)

        include_script = get_optional_param(request.GET, 'include_script',
                                            False, Bool)
        filters = get_optional_param(request.GET, 'filters', None, String)
        if filters is not None:
            filters = set(filters.split(','))

        ret = []
        for script in qs:
            if (filters is not None and script.name not in filters
                    and filters.isdisjoint(script.tags)):
                continue
            else:
                script.include_script = include_script
                ret.append(script)

        return ret
Esempio n. 4
0
    def read(self, request, system_id):
        """Return a list of script results grouped by run.

        :param type: Only return scripts with the given type. This can be
                     commissioning, testing, or installion. Defaults to showing
                     all.
        :type type: unicode

        :param hardware_type: Only return scripts for the given hardware type.
            Can be node, cpu, memory, or storage. Defaults to all.
        :type script_type: unicode

        :param include_output: Include base64 encoded output from the script.
        :type include_output: bool

        :param filters: A comma seperated list to show only results
                        with a script name or tag.
        :type filters: unicode
        """
        node = Node.objects.get_node_or_404(system_id=system_id,
                                            user=request.user,
                                            perm=NODE_PERMISSION.VIEW)
        result_type = get_optional_param(request.GET, 'type')
        include_output = get_optional_param(request.GET, 'include_output',
                                            False, Bool)
        filters = get_optional_param(request.GET, 'filters', None, String)
        if filters is not None:
            filters = filters.split(',')
        if result_type is not None:
            try:
                result_type = translate_result_type(result_type)
            except ValidationError as e:
                raise MAASAPIValidationError(e)
            else:
                qs = ScriptSet.objects.filter(node=node,
                                              result_type=result_type)
        else:
            qs = ScriptSet.objects.filter(node=node)

        hardware_type = get_optional_param(request.GET, 'hardware_type')
        if hardware_type is not None:
            try:
                hardware_type = translate_hardware_type(hardware_type)
            except ValidationError as e:
                raise MAASAPIValidationError(e)

        ret = []
        for script_set in qs:
            script_set.include_output = include_output
            script_set.filters = filters
            script_set.hardware_type = hardware_type
            ret.append(script_set)
        return ret
Esempio n. 5
0
    def read(self, request, system_id, id):
        """@description-title Get specific script result
        @description View a set of test results for a given system_id and
        script id.

        "id" can either by the script set id, ``current-commissioning``,
        ``current-testing``, or ``current-installation``.

        @param (string) "{system_id}" [required=true] The machine's system_id.
        @param (string) "{id}" [required=true] The script result id.

        @param (string) "hardware_type" [required=false] Only return scripts
        for the given hardware type.  Can be ``node``, ``cpu``, ``memory``, or
        ``storage``.  Defaults to all.

        @param (string) "include_output" [required=false] Include the base64
        encoded output from the script if any value for include_output is
        given.

        @param (string) "filters" [required=false] A comma seperated list to
        show only results that ran with a script name, tag, or id.

        @success (http-status-code) "server-success" 200
        @success (json) "success-json" A JSON object containing the requested
        script result object.
        @success-example "success-json" [exkey=script-results-read-by-id]
        placeholder text

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested machine or script result is
        not found.
        @error-example "not-found"
            Not Found
        """
        script_set = self._get_script_set(request, system_id, id)
        include_output = get_optional_param(request.GET, "include_output",
                                            False, Bool)
        filters = get_optional_param(request.GET, "filters", None, String)
        if filters is not None:
            filters = filters.split(",")
        hardware_type = get_optional_param(request.GET, "hardware_type")
        if hardware_type is not None:
            try:
                hardware_type = translate_hardware_type(hardware_type)
            except ValidationError as e:
                raise MAASAPIValidationError(e)
        script_set.include_output = include_output
        script_set.filters = filters
        script_set.hardware_type = hardware_type
        return script_set
Esempio n. 6
0
    def _store_results(self, node, script_set, request, status):
        """Store uploaded results."""
        # Group files together with the ScriptResult they belong.
        results = {}
        for script_name, uploaded_file in request.FILES.items():
            content = uploaded_file.read()
            process_file(results, script_set, script_name, content,
                         request.POST)

        # Commit results to the database.
        for script_result, args in results.items():
            script_result.store_result(**args,
                                       timedout=(
                                           status == SIGNAL_STATUS.TIMEDOUT))

        script_set.last_ping = datetime.now()
        script_set.save()

        if status == SIGNAL_STATUS.WORKING:
            script_result_id = get_optional_param(request.POST,
                                                  'script_result_id', None,
                                                  Int)
            if script_result_id is not None:
                script_result = script_set.find_script_result(script_result_id)
                # Only update the script status if it was in a pending state
                # incase the script result has been uploaded and proceeded
                # already.
                if script_result.status == SCRIPT_STATUS.PENDING:
                    script_result.status = SCRIPT_STATUS.RUNNING
                    script_result.save()
Esempio n. 7
0
    def create(self, request):
        """Add a new SSH key to the requesting or supplied user's account.

        The request payload should contain the public SSH key data in form
        data whose name is "key".
        """
        user = request.user
        username = get_optional_param(request.POST, 'user')
        if username is not None and request.user.is_superuser:
            supplied_user = get_one(User.objects.filter(username=username))
            if supplied_user is not None:
                user = supplied_user
            else:
                # Raise an error so that the user can know that their
                # attempt at specifying a user did not work.
                raise MAASAPIValidationError(
                    "Supplied username does not match any current users.")
        elif username is not None and not request.user.is_superuser:
            raise MAASAPIValidationError(
                "Only administrators can specify a user"
                " when creating an SSH key.")

        form = SSHKeyForm(user=user, data=request.data)
        if form.is_valid():
            sshkey = form.save(ENDPOINT.API, request)
            emitter = JSONEmitter(sshkey, typemapper, None,
                                  DISPLAY_SSHKEY_FIELDS)
            stream = emitter.render(request)
            return HttpResponse(stream,
                                content_type='application/json; charset=utf-8',
                                status=int(http.client.CREATED))
        else:
            raise MAASAPIValidationError(form.errors)
Esempio n. 8
0
    def read(self, request):
        """List all resources for the specified criteria.

        :param domain: restrict the listing to entries for the domain.
        :param name: restrict the listing to entries of the given name.
        :param rrtype: restrict the listing to entries which have
            records of the given rrtype.
        :param all: if True, also include implicit DNS records created for
            nodes registered in MAAS.
        """
        data = request.GET
        fqdn = data.get('fqdn', None)
        name = data.get('name', None)
        domainname = data.get('domain', None)
        rrtype = data.get('rrtype', None)
        if rrtype is not None:
            rrtype = rrtype.upper()
        _all = get_optional_param(request.GET,
                                  'all',
                                  default=False,
                                  validator=StringBool)
        if domainname is None and name is None and fqdn is not None:
            # We need a type for resource separation.  If the user didn't give
            # us a rrtype, then assume it's an address of some sort.
            (name, domainname) = separate_fqdn(fqdn, rrtype)
        user = request.user
        return get_dnsresource_queryset(_all, domainname, name, rrtype, user)
Esempio n. 9
0
    def delete(self, request, system_id):
        """Delete a specific rack controller.

        A rack controller cannot be deleted if it is set to `primary_rack` on
        a `VLAN` and another rack controller cannot be used to provide DHCP for
        said VLAN. Use `force` to override this behavior.

        Using `force` will also allow deleting a rack controller that is
        hosting pod virtual machines. (The pod will also be deleted.)

        Rack controllers that are also region controllers will be converted
        to a region controller (and hosted pods will not be affected).

        :param force: Always delete the rack controller even if its the
            `primary_rack` on a `VLAN` and another rack controller cannot
            provide DHCP on said VLAN. This will disable DHCP on those VLANs.
        :type force: boolean

        Returns 404 if the node is not found.
        Returns 403 if the user does not have permission to delete the node.
        Returns 400 if the node cannot be deleted.
        Returns 204 if the node is successfully deleted.
        """
        node = self.model.objects.get_node_or_404(system_id=system_id,
                                                  user=request.user,
                                                  perm=NodePermission.admin)
        node.as_self().delete(
            force=get_optional_param(request.GET, 'force', False, StringBool))
        return rc.DELETED
Esempio n. 10
0
    def create_authorisation_token(self, request):
        """Create an authorisation OAuth token and OAuth consumer.

        :param name: Optional name of the token that will be generated.
        :type name: unicode
        :return: a json dict with four keys: 'token_key',
            'token_secret', 'consumer_key'  and 'name'(e.g.
            {token_key: 's65244576fgqs', token_secret: 'qsdfdhv34',
            consumer_key: '68543fhj854fg', name: 'MAAS consumer'}).
        :rtype: string (json)

        """
        profile = request.user.userprofile
        consumer_name = get_optional_param(request.data, 'name')
        consumer, token = profile.create_authorisation_token(consumer_name)
        create_audit_event(
            EVENT_TYPES.AUTHORISATION, ENDPOINT.API,
            request, None, "Created token.")
        auth_info = {
            'token_key': token.key, 'token_secret': token.secret,
            'consumer_key': consumer.key, 'name': consumer.name
        }
        return HttpResponse(
            json.dumps(auth_info),
            content_type='application/json; charset=utf-8',
            status=int(http.client.OK))
Esempio n. 11
0
    def create_authorisation_token(self, request):
        """@description-title Create an authorisation token
        @description Create an authorisation OAuth token and OAuth consumer.

        @param (string) "name" [required=false] Optional name of the token that
        will be generated.

        @success (http-status-code) "200" 200
        @success (json) "success-json" A JSON object containing: ``token_key``,
        ``token_secret``, ``consumer_key``, and ``name``.
        @success-example "success-json" [exkey=account-create-token]
        placeholder text
        """
        profile = request.user.userprofile
        consumer_name = get_optional_param(request.data, "name")
        consumer, token = profile.create_authorisation_token(consumer_name)
        create_audit_event(
            EVENT_TYPES.AUTHORISATION,
            ENDPOINT.API,
            request,
            None,
            "Created token.",
        )
        auth_info = {
            "token_key": token.key,
            "token_secret": token.secret,
            "consumer_key": consumer.key,
            "name": consumer.name,
        }
        return HttpResponse(
            json.dumps(auth_info),
            content_type="application/json; charset=utf-8",
            status=int(http.client.OK),
        )
Esempio n. 12
0
    def read(self, request, name):
        """@description-title Return script metadata
        @description Return metadata belonging to the script with the given
        name.

        @param (string) "{name}" [required=true] The script's name.

        @param (string) "include_script" [required=false] Include the base64
        encoded script content if any value is given for include_script.

        @success (http-status-code) "server-success" 200
        @success (json) "success-json" A JSON object containing information
        about the script.
        @success-example "success-json" [exkey=scripts-read-by-name]
        placeholder text

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested script is not found.
        @error-example "not-found"
            Not Found
        """
        if name.isdigit():
            script = get_object_or_404(Script, id=int(name))
        else:
            script = get_object_or_404(Script, name=name)
        script.include_script = get_optional_param(request.GET,
                                                   'include_script', False,
                                                   Bool)
        return script
Esempio n. 13
0
    def power_off(self, request, system_id):
        """Power off a node.

        :param stop_mode: An optional power off mode. If 'soft',
            perform a soft power down if the node's power type supports
            it, otherwise perform a hard power off. For all values other
            than 'soft', and by default, perform a hard power off. A
            soft power off generally asks the OS to shutdown the system
            gracefully before powering off, while a hard power off
            occurs immediately without any warning to the OS.
        :type stop_mode: unicode
        :param comment: Optional comment for the event log.
        :type comment: unicode

        Returns 404 if the node is not found.
        Returns 403 if the user does not have permission to stop the node.
        """
        stop_mode = request.POST.get('stop_mode', 'hard')
        comment = get_optional_param(request.POST, 'comment')
        node = self.model.objects.get_node_or_404(
            system_id=system_id, user=request.user,
            perm=NodePermission.edit)
        power_action_sent = node.stop(
            request.user, stop_mode=stop_mode, comment=comment)
        if power_action_sent:
            return node
        else:
            return None
Esempio n. 14
0
    def ip_addresses(self, request, id):
        """@description-title Summary of IP addresses
        @description Returns a summary of IP addresses assigned to this subnet.

        @param (int) "{id}" [required=true] A subnet ID.

        @param (int) "with_username" [required=false] If '0', suppresses the
        display of usernames associated with each address. '1' == True, '0' ==
        False. (Default: '1')

        @param (int) "with_summary" [required=false] If '0', suppresses the
        display of nodes, BMCs, and and DNS records associated with each
        address. '1' == True, '0' == False. (Default: True)

        @param (int) "with_node_summary" [required=false] Deprecated. Use
        'with_summary'.

        @success (http-status-code) "server-success" 200
        @success (json) "success-json" A JSON object containing a list of IP
        addresses and information about each.
        @success-example "success-json" [exkey=subnets-ip-addresses]
        placeholder text

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested subnet is not found.
        @error-example "not-found"
            Not Found
        """
        subnet = Subnet.objects.get_subnet_or_404(id, request.user,
                                                  NodePermission.view)
        with_username = get_optional_param(request.GET,
                                           'with_username',
                                           default=True,
                                           validator=StringBool)
        with_summary = get_optional_param(request.GET,
                                          'with_summary',
                                          True,
                                          validator=StringBool)
        with_node_summary = get_optional_param(request.GET,
                                               'with_node_summary',
                                               True,
                                               validator=StringBool)
        # Handle deprecated with_node_summary parameter.
        if with_node_summary is False:
            with_summary = False
        return subnet.render_json_for_related_ips(with_username=with_username,
                                                  with_summary=with_summary)
Esempio n. 15
0
    def clear(self, request, **kwargs):
        """@description-title Delete all discovered neighbours
        @description Deletes all discovered neighbours and/or mDNS entries.

        Note: One of ``mdns``, ``neighbours``, or ``all`` parameters must be
        supplied.

        @param (boolean) "mdns" [required=false] Delete all mDNS entries.

        @param (boolean) "neighbours" [required=false] Delete all neighbour
        entries.

        @param (boolean) "all" [required=false] Delete all discovery data.

        @success (http-status-code) "server-success" 204
        """
        all = get_optional_param(request.POST,
                                 "all",
                                 default=False,
                                 validator=StringBool)
        mdns = get_optional_param(request.POST,
                                  "mdns",
                                  default=False,
                                  validator=StringBool)
        neighbours = get_optional_param(request.POST,
                                        "neighbours",
                                        default=False,
                                        validator=StringBool)

        if not request.user.has_perm(NodePermission.admin, Discovery):
            response = HttpResponseForbidden(
                content_type="text/plain",
                content="Must be an administrator to clear discovery entries.",
            )
            return response
        if True not in (mdns, neighbours, all):
            content = dedent("""\
                Bad request: could not determine what data to clear.
                Must specify mdns=True, neighbours=True, or all=True.""")
            response = HttpResponseBadRequest(content_type="text/plain",
                                              content=content)
            return response
        Discovery.objects.clear(user=request.user,
                                all=all,
                                mdns=mdns,
                                neighbours=neighbours)
        return rc.DELETED
Esempio n. 16
0
    def read(self, request, system_id):
        """Return a list of script results grouped by run.

        :param type: Only return scripts with the given type. This can be
                     commissioning, testing, or installion. Defaults to showing
                     all.
        :type type: unicode

        :param include_output: Include base64 encoded output from the script.
        :type include_output: bool

        :param filters: A comma seperated list to show only results
                        with a script name or tag.
        :type filters: unicode
        """
        node = Node.objects.get_node_or_404(system_id=system_id,
                                            user=request.user,
                                            perm=NODE_PERMISSION.VIEW)
        result_type = get_optional_param(request.GET, 'type')
        include_output = get_optional_param(request.GET, 'include_output',
                                            False, Bool)
        filters = get_optional_param(request.GET, 'filters', None, String)
        if filters is not None:
            filters = filters.split(',')
        if result_type is not None:
            if result_type.isdigit():
                result_type = int(result_type)
            elif result_type in ['test', 'testing']:
                result_type = RESULT_TYPE.TESTING
            elif result_type in ['commission', 'commissioning']:
                result_type = RESULT_TYPE.COMMISSIONING
            elif result_type in ['install', 'installation']:
                result_type = RESULT_TYPE.INSTALLATION
            else:
                raise MAASAPIValidationError(
                    'Unknown type "%s": type must be the type numeric value '
                    'testing, commissioning, or installation.')
            qs = ScriptSet.objects.filter(node=node, result_type=result_type)
        else:
            qs = ScriptSet.objects.filter(node=node)
        ret = []
        for script_set in qs:
            script_set.include_output = include_output
            script_set.filters = filters
            ret.append(script_set)
        return ret
Esempio n. 17
0
    def read(self, request):
        """Return a list of stored scripts.

        :param type: Only return scripts with the given type. This can be
            testing or commissioning. Defaults to showing both.
        :type script_type: unicode

        :param include_script: Include the base64 encoded script content.
        :type include_script: bool

        :param filters: A comma seperated list to show only results
                        with a script name or tag.
        :type filters: unicode
        """
        qs = Script.objects.all()

        script_type = get_optional_param(request.GET, 'type')
        if script_type is not None:
            if script_type.isdigit():
                script_type = int(script_type)
            elif script_type in ['test', 'testing']:
                script_type = SCRIPT_TYPE.TESTING
            elif script_type in ['commission', 'commissioning']:
                script_type = SCRIPT_TYPE.COMMISSIONING
            else:
                raise MAASAPIValidationError('Unknown script type')
            qs = qs.filter(script_type=script_type)

        include_script = get_optional_param(request.GET, 'include_script',
                                            False, Bool)
        filters = get_optional_param(request.GET, 'filters', None, String)
        if filters is not None:
            filters = set(filters.split(','))

        ret = []
        for script in qs:
            if (filters is not None and script.name not in filters
                    and filters.isdisjoint(script.tags)):
                continue
            else:
                script.include_script = include_script
                ret.append(script)

        return ret
Esempio n. 18
0
    def create(self, request):
        """@description-title Add a new SSH key
        @description Add a new SSH key to the requesting or supplied user's
        account.

        @param (string) "key" [required=true,formatting=true] A public SSH key
        should be provided in the request payload as form data with the name
        'key':

            key: "key-type public-key-data"

        - ``key-type``: ecdsa-sha2-nistp256, ecdsa-sha2-nistp384,
          ecdsa-sha2-nistp521, ssh-dss, ssh-ed25519, ssh-rsa
        - ``public key data``: Base64-encoded key data.

        @success (http-status-code) "201" 201
        @success (json) "success-json" A JSON object containing the new key.
        @success-example "success-json" [exkey=ssh-keys-create] placeholder
        text
        """
        user = request.user
        username = get_optional_param(request.POST, "user")
        if username is not None and request.user.is_superuser:
            supplied_user = get_one(User.objects.filter(username=username))
            if supplied_user is not None:
                user = supplied_user
            else:
                # Raise an error so that the user can know that their
                # attempt at specifying a user did not work.
                raise MAASAPIValidationError(
                    "Supplied username does not match any current users."
                )
        elif username is not None and not request.user.is_superuser:
            raise MAASAPIValidationError(
                "Only administrators can specify a user"
                " when creating an SSH key."
            )

        form = SSHKeyForm(user=user, data=request.data)
        if form.is_valid():
            sshkey = form.save(ENDPOINT.API, request)
            emitter = JSONEmitter(
                sshkey, typemapper, None, DISPLAY_SSHKEY_FIELDS
            )
            stream = emitter.render(request)
            return HttpResponse(
                stream,
                content_type="application/json; charset=utf-8",
                status=int(http.client.CREATED),
            )
        else:
            raise MAASAPIValidationError(form.errors)
Esempio n. 19
0
    def statistics(self, request, id):
        """\
        Returns statistics for the specified subnet, including:

        num_available: the number of available IP addresses
        largest_available: the largest number of contiguous free IP addresses
        num_unavailable: the number of unavailable IP addresses
        total_addresses: the sum of the available plus unavailable addresses
        usage: the (floating point) usage percentage of this subnet
        usage_string: the (formatted unicode) usage percentage of this subnet
        ranges: the specific IP ranges present in ths subnet (if specified)

        Optional parameters
        -------------------

        include_ranges
           If True, includes detailed information
           about the usage of this range.

        include_suggestions
          If True, includes the suggested gateway and dynamic range for this
          subnet, if it were to be configured.

        Returns 404 if the subnet is not found.
        """
        subnet = Subnet.objects.get_subnet_or_404(id, request.user,
                                                  NodePermission.view)
        include_ranges = get_optional_param(request.GET,
                                            'include_ranges',
                                            default=False,
                                            validator=StringBool)
        include_suggestions = get_optional_param(request.GET,
                                                 'include_suggestions',
                                                 default=False,
                                                 validator=StringBool)
        full_iprange = subnet.get_iprange_usage()
        statistics = IPRangeStatistics(full_iprange)
        return statistics.render_json(include_ranges=include_ranges,
                                      include_suggestions=include_suggestions)
Esempio n. 20
0
    def read(self, request, system_id, id):
        """View a specific set of results.

        id can either by the script set id, current-commissioning,
        current-testing, or current-installation.

        :param include_output: Include base64 encoded output from the script.
        :type include_output: bool

        :param filters: A comma seperated list to show only results that ran
                        with a script name, tag, or id.
        :type filters: unicode
        """
        script_set = self._get_script_set(request, system_id, id)
        include_output = get_optional_param(request.GET, 'include_output',
                                            False, Bool)
        filters = get_optional_param(request.GET, 'filters', None, String)
        if filters is not None:
            filters = filters.split(',')
        script_set.include_output = include_output
        script_set.filters = filters
        return script_set
Esempio n. 21
0
    def download(self, request, name):
        """Download a script.

        :param revision: What revision to download, latest by default. Can use
            rev as a shortcut.
        :type revision: integer
        """
        if name.isdigit():
            script = get_object_or_404(Script, id=int(name))
        else:
            script = get_object_or_404(Script, name=name)
        revision = get_optional_param(request.GET, 'revision', None, Int)
        if revision is None:
            revision = get_optional_param(request.GET, 'rev', None, Int)
        if revision is not None:
            for rev in script.script.previous_versions():
                if rev.id == revision:
                    return HttpResponse(rev.data,
                                        content_type='application/binary')
            raise MAASAPIValidationError("%s not found in history" % revision)
        else:
            return HttpResponse(script.script.data,
                                content_type='application/binary')
Esempio n. 22
0
    def read(self, request, name):
        """Return a script's metadata.

        :param include_script: Include the base64 encoded script content.
        :type include_script: bool
        """
        if name.isdigit():
            script = get_object_or_404(Script, id=int(name))
        else:
            script = get_object_or_404(Script, name=name)
        script.include_script = get_optional_param(request.GET,
                                                   'include_script', False,
                                                   Bool)
        return script
Esempio n. 23
0
    def ip_addresses(self, request, id):
        """\
        Returns a summary of IP addresses assigned to this subnet.

        Optional parameters
        -------------------

        with_username
          If False, suppresses the display of usernames associated with each
          address. (Default: True)

        with_summary
          If False, suppresses the display of nodes, BMCs, and and DNS records
          associated with each address. (Default: True)

        with_node_summary
          Deprecated form of with_summary.
        """
        subnet = Subnet.objects.get_subnet_or_404(id, request.user,
                                                  NodePermission.view)
        with_username = get_optional_param(request.GET,
                                           'with_username',
                                           default=True,
                                           validator=StringBool)
        with_summary = get_optional_param(request.GET,
                                          'with_summary',
                                          True,
                                          validator=StringBool)
        with_node_summary = get_optional_param(request.GET,
                                               'with_node_summary',
                                               True,
                                               validator=StringBool)
        # Handle deprecated with_node_summary parameter.
        if with_node_summary is False:
            with_summary = False
        return subnet.render_json_for_related_ips(with_username=with_username,
                                                  with_summary=with_summary)
Esempio n. 24
0
    def clear(self, request, **kwargs):
        """Deletes all discovered neighbours and/or mDNS entries.

        :param mdns: if True, deletes all mDNS entries.
        :param neighbours: if True, deletes all neighbour entries.
        :param all: if True, deletes all discovery data.
        """
        all = get_optional_param(request.POST,
                                 'all',
                                 default=False,
                                 validator=StringBool)
        mdns = get_optional_param(request.POST,
                                  'mdns',
                                  default=False,
                                  validator=StringBool)
        neighbours = get_optional_param(request.POST,
                                        'neighbours',
                                        default=False,
                                        validator=StringBool)

        if not request.user.has_perm(NodePermission.admin, Discovery):
            response = HttpResponseForbidden(
                content_type='text/plain',
                content="Must be an administrator to clear discovery entries.")
            return response
        if True not in (mdns, neighbours, all):
            content = dedent("""\
                Bad request: could not determine what data to clear.
                Must specify mdns=True, neighbours=True, or all=True.""")
            response = HttpResponseBadRequest(content_type='text/plain',
                                              content=content)
            return response
        Discovery.objects.clear(user=request.user,
                                all=all,
                                mdns=mdns,
                                neighbours=neighbours)
        return rc.DELETED
Esempio n. 25
0
    def power_on(self, request, system_id):
        """@description-title Turn on a node
        @description Turn on the given node with optional user-data and
        comment.

        @param (string) "user_data" [required=false] Base64-encoded blob of
        data to be made available to the nodes through the metadata service.

        @param (string) "comment" [required=false] Comment for the event log.

        @success (http-status-code) "204" 204
        @success (json) "success_json" A JSON object containing the node's
        information.
        @success-example "success_json" [exkey=power-on] placeholder text

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested node is not found.
        @error-example "not-found"
            Not Found

        @error (http-status-code) "403" 403
        @error (content) "no-perms" The user is not authorized to power on the
        node.

        @error (http-status-code) "503" 503
        @error (content) "no-ips" Returns 503 if the start-up attempted to
        allocate an IP address, and there were no IP addresses available on the
        relevant cluster interface.
        """
        user_data = request.POST.get('user_data', None)
        comment = get_optional_param(request.POST, 'comment')

        node = self.model.objects.get_node_or_404(
            system_id=system_id, user=request.user,
            perm=NodePermission.edit)
        if node.owner is None and node.node_type != NODE_TYPE.RACK_CONTROLLER:
            raise NodeStateViolation(
                "Can't start node: it hasn't been allocated.")
        if user_data is not None:
            user_data = b64decode(user_data)
        try:
            node.start(request.user, user_data=user_data, comment=comment)
        except StaticIPAddressExhaustion:
            # The API response should contain error text with the
            # system_id in it, as that is the primary API key to a node.
            raise StaticIPAddressExhaustion(
                "%s: Unable to allocate static IP due to address"
                " exhaustion." % system_id)
        return node
Esempio n. 26
0
    def download(self, request, name):
        """@description-title Download a script
        @description Download a script with the given name.

        @param (string) "{name}" [required=true] The name of the script.

        @param (int) "revision" [required=false] What revision to download,
        latest by default. Can use rev as a shortcut.

        @success (http-status-code) "server-success" 200
        @success (content) "success-text" A plain-text representation of the
        requested script.
        @success-example "success-text" [exkey=scripts-download] placeholder
        text

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested script is not found.
        @error-example "not-found"
            Not Found
        """
        if name.isdigit():
            script = get_object_or_404(Script, id=int(name))
        else:
            script = get_object_or_404(Script, name=name)
        revision = get_optional_param(request.GET, 'revision', None, Int)
        if revision is None:
            revision = get_optional_param(request.GET, 'rev', None, Int)
        if revision is not None:
            for rev in script.script.previous_versions():
                if rev.id == revision:
                    return HttpResponse(rev.data,
                                        content_type='application/binary')
            raise MAASAPIValidationError("%s not found in history" % revision)
        else:
            return HttpResponse(script.script.data,
                                content_type='application/binary')
Esempio n. 27
0
    def override_failed_testing(self, request, system_id):
        """Ignore failed tests and put node back into a usable state.

        :param comment: Optional comment for the event log.
        :type comment: unicode

        Returns 404 if the machine is not found.
        Returns 403 if the user does not have permission to ignore tests for
        the node.
        """
        comment = get_optional_param(request.POST, 'comment')
        node = self.model.objects.get_node_or_404(
            user=request.user, system_id=system_id, perm=NodePermission.admin)
        node.override_failed_testing(request.user, comment)
        return node
Esempio n. 28
0
    def abort(self, request, system_id):
        """Abort a node's current operation.

        :param comment: Optional comment for the event log.
        :type comment: unicode

        Returns 404 if the node could not be found.
        Returns 403 if the user does not have permission to abort the
        current operation.
        """
        comment = get_optional_param(request.POST, 'comment')
        node = self.model.objects.get_node_or_404(
            system_id=system_id, user=request.user,
            perm=NodePermission.edit)
        node.abort_operation(request.user, comment)
        return node
Esempio n. 29
0
    def delete(self, request, system_id):
        """Delete a specific region controller.

        A region controller cannot be deleted if it hosts pod virtual machines.
        Use `force` to override this behavior. Forcing deletion will also
        remove hosted pods.

        Returns 404 if the node is not found.
        Returns 403 if the user does not have permission to delete the node.
        Returns 400 if the node cannot be deleted.
        Returns 204 if the node is successfully deleted.
        """
        node = self.model.objects.get_node_or_404(system_id=system_id,
                                                  user=request.user,
                                                  perm=NodePermission.admin)
        node.as_self().delete(
            force=get_optional_param(request.GET, 'force', False, StringBool))
        return rc.DELETED
Esempio n. 30
0
    def read(self, request):
        """@description-title List resources
        @description List all resources for the specified criteria.

        @param (string) "domain" [required=false] Restricts the listing to
        entries for the domain.

        @param (string) "name" [required=false] Restricts the listing to
        entries of the given name.

        @param (string) "rrtype" [required=false] Restricts the listing to
        entries which have records of the given rrtype.

        @param (boolean) "all" [required=false] Include implicit DNS records
        created for nodes registered in MAAS if true.

        @success (http-status-code) "server-success" 200
        @success (json) "success-json" A JSON object containing a list of the
        requested DNS resource objects.
        @success-example "success-json" [exkey=dnsresources-read] placeholder
        text

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested DNS resources are not found.
        @error-example "not-found"
            Not Found
        """
        data = request.GET
        fqdn = data.get("fqdn", None)
        name = data.get("name", None)
        domainname = data.get("domain", None)
        rrtype = data.get("rrtype", None)
        if rrtype is not None:
            rrtype = rrtype.upper()
        _all = get_optional_param(
            request.GET, "all", default=False, validator=StringBool
        )
        if domainname is None and name is None and fqdn is not None:
            # We need a type for resource separation.  If the user didn't give
            # us a rrtype, then assume it's an address of some sort.
            (name, domainname) = separate_fqdn(fqdn, rrtype)
        user = request.user
        return get_dnsresource_queryset(_all, domainname, name, rrtype, user)
Esempio n. 31
0
    def mark_broken(self, request, system_id):
        """Mark a node as 'broken'.

        If the node is allocated, release it first.

        :param error_description: An optional description of the reason the
            node is being marked broken.
        :type error_description: unicode

        Returns 404 if the node is not found.
        Returns 403 if the user does not have permission to mark the node
        broken.
        """
        node = Node.objects.get_node_or_404(
            user=request.user, system_id=system_id, perm=NODE_PERMISSION.EDIT)
        error_description = get_optional_param(
            request.POST, 'error_description', '')
        node.mark_broken(error_description)
        return node