예제 #1
0
    def create(self, request):
        """@description-title Create a new device
        @description Create a new device.

        @param (string) "hostname" [required=false] A hostname. If not given,
        one will be generated.

        @param (string) "domain" [required=false] The domain of the device. If
        not given the default domain is used.

        @param (string) "mac_addresses" [required=true] One or more MAC
        addresses for the device.

        @param (string) "parent" [required=false] The system id of the parent.

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

        @error (http-status-code) "400" 400
        @error (content) "bad-param" There was a problem with the given
        parameters.
        """
        form = DeviceWithMACsForm(data=request.data, request=request)
        if form.is_valid():
            device = form.save()
            parent = device.parent
            maaslog.info(
                "%s: Added new device%s", device.hostname,
                "" if not parent else " (parent: %s)" % parent.hostname)
            return device
        else:
            raise MAASAPIValidationError(form.errors)
예제 #2
0
    def create(self, request):
        """Create a new device.

        :param hostname: A hostname. If not given, one will be generated.
        :type hostname: unicode

        :param domain: The domain of the device. If not given the default
            domain is used.
        :type domain: unicode

        :param mac_addresses: One or more MAC addresses for the device.
        :type mac_addresses: unicode

        :param parent: The system id of the parent.  Optional.
        :type parent: unicode
        """
        form = DeviceWithMACsForm(data=request.data, request=request)
        if form.is_valid():
            device = form.save()
            parent = device.parent
            maaslog.info(
                "%s: Added new device%s", device.hostname,
                "" if not parent else " (parent: %s)" % parent.hostname)
            return device
        else:
            raise MAASAPIValidationError(form.errors)
예제 #3
0
    def claim_sticky_ip_address(self, request, system_id):
        """Assign a "sticky" IP address to a Node's MAC.

        This method is reserved for admin users.

        :param mac_address: Optional MAC address on the node on which to
            assign the sticky IP address.  If not passed, defaults to the
            primary MAC for the node.
        :param requested_address: Optional IP address to claim.  Must be in
            the range defined on a cluster interface to which the context
            MAC is related, or 403 Forbidden is returned.  If the requested
            address is unavailable for use, 404 Not Found is returned.

        A sticky IP is one which stays with the node until the IP is
        disassociated with the node, or the node is deleted.  It allows
        an admin to give a node a stable IP, since normally an automatic
        IP is allocated to a node only during the time a user has
        acquired and started a node.

        Returns 404 if the node is not found.
        Returns 409 if the node is in an allocated state.
        Returns 400 if the mac_address is not found on the node.
        Returns 503 if there are not enough IPs left on the cluster interface
        to which the mac_address is linked.
        """
        node = get_object_or_404(Node, system_id=system_id)
        if node.status == NODE_STATUS.ALLOCATED:
            raise NodeStateViolation(
                "Sticky IP cannot be assigned to a node that is allocated")

        raw_mac = request.POST.get('mac_address', None)
        if raw_mac is None:
            mac_address = node.get_primary_mac()
        else:
            try:
                mac_address = MACAddress.objects.get(
                    mac_address=raw_mac, node=node)
            except MACAddress.DoesNotExist:
                raise MAASAPIBadRequest(
                    "mac_address %s not found on the node" % raw_mac)
        requested_address = request.POST.get('requested_address', None)
        sticky_ips = mac_address.claim_static_ips(
            alloc_type=IPADDRESS_TYPE.STICKY,
            requested_address=requested_address)
        claims = [
            (static_ip.ip, mac_address.mac_address.get_raw())
            for static_ip in sticky_ips]
        node.update_host_maps(claims)
        change_dns_zones(node.nodegroup)
        maaslog.info(
            "%s: Sticky IP address(es) allocated: %s", node.hostname,
            ', '.join(allocation.ip for allocation in sticky_ips))
        return node
예제 #4
0
    def mark_fixed(self, request, system_id):
        """Mark a broken node as fixed and set its status as 'ready'.

        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.ADMIN)
        node.mark_fixed()
        maaslog.info(
            "%s: User %s marked node as fixed", node.hostname,
            request.user.username)
        return node
예제 #5
0
    def acquire(self, request):
        """Acquire an available node for deployment.

        Constraints parameters can be used to acquire a node that possesses
        certain characteristics.  All the constraints are optional and when
        multiple constraints are provided, they are combined using 'AND'
        semantics.

        :param name: Hostname of the returned node.
        :type name: unicode
        :param arch: Architecture of the returned node (e.g. 'i386/generic',
            'amd64', 'armhf/highbank', etc.).
        :type arch: unicode
        :param cpu_count: The minium number of CPUs the returned node must
            have.
        :type cpu_count: int
        :param mem: The minimum amount of memory (expressed in MB) the
             returned node must have.
        :type mem: float
        :param tags: List of tags the returned node must have.
        :type tags: list of unicodes
        :param not_tags: List of tags the acquired node must not have.
        :type tags: List of unicodes.
        :param connected_to: List of routers' MAC addresses the returned
            node must be connected to.
        :type connected_to: unicode or list of unicodes
        :param networks: List of networks (defined in MAAS) to which the node
            must be attached.  A network can be identified by the name
            assigned to it in MAAS; or by an `ip:` prefix followed by any IP
            address that falls within the network; or a `vlan:` prefix
            followed by a numeric VLAN tag, e.g. `vlan:23` for VLAN number 23.
            Valid VLAN tags must be in the range of 1 to 4095 inclusive.
        :type networks: list of unicodes
        :param not_networks: List of networks (defined in MAAS) to which the
            node must not be attached.  The returned noded won't be attached to
            any of the specified networks.  A network can be identified by the
            name assigned to it in MAAS; or by an `ip:` prefix followed by any
            IP address that falls within the network; or a `vlan:` prefix
            followed by a numeric VLAN tag, e.g. `vlan:23` for VLAN number 23.
            Valid VLAN tags must be in the range of 1 to 4095 inclusive.
        :type not_networks: list of unicodes
        :param not_connected_to: List of routers' MAC Addresses the returned
            node must not be connected to.
        :type connected_to: list of unicodes
        :param zone: An optional name for a physical zone the acquired
            node should be located in.
        :type zone: unicode
        :type not_in_zone: Optional list of physical zones from which the
            node should not be acquired.
        :type not_in_zone: List of unicodes.
        :param agent_name: An optional agent name to attach to the
            acquired node.
        :type agent_name: unicode

        Returns 409 if a suitable node matching the constraints could not be
        found.
        """
        form = AcquireNodeForm(data=request.data)
        maaslog.info(
            "Request from user %s to acquire a node with constraints %s",
            request.user.username, request.data)

        if not form.is_valid():
            raise ValidationError(form.errors)

        # This lock prevents a node we've picked as available from
        # becoming unavailable before our transaction commits.
        with locks.node_acquire:
            nodes = Node.objects.get_available_nodes_for_acquisition(
                request.user)
            nodes = form.filter_nodes(nodes)
            node = get_first(nodes)
            if node is None:
                constraints = form.describe_constraints()
                if constraints == '':
                    # No constraints.  That means no nodes at all were
                    # available.
                    message = "No node available."
                else:
                    message = (
                        "No available node matches constraints: %s"
                        % constraints)
                raise NodesNotAvailable(message)
            agent_name = request.data.get('agent_name', '')
            node.acquire(
                request.user, get_oauth_token(request),
                agent_name=agent_name)
            return node
예제 #6
0
def create_node(request):
    """Service an http request to create a node.

    The node will be in the New state.

    :param request: The http request for this node to be created.
    :return: A `Node`.
    :rtype: :class:`maasserver.models.Node`.
    :raises: ValidationError
    """

    # For backwards compatibilty reasons, requests may be sent with:
    #     architecture with a '/' in it: use normally
    #     architecture without a '/' and no subarchitecture: assume 'generic'
    #     architecture without a '/' and a subarchitecture: use as specified
    #     architecture with a '/' and a subarchitecture: error
    given_arch = request.data.get('architecture', None)
    given_subarch = request.data.get('subarchitecture', None)
    altered_query_data = request.data.copy()
    if given_arch and '/' in given_arch:
        if given_subarch:
            # Architecture with a '/' and a subarchitecture: error.
            raise ValidationError('Subarchitecture cannot be specified twice.')
        # Architecture with a '/' in it: use normally.
    elif given_arch:
        if given_subarch:
            # Architecture without a '/' and a subarchitecture:
            # use as specified.
            altered_query_data['architecture'] = '/'.join(
                [given_arch, given_subarch])
            del altered_query_data['subarchitecture']
        else:
            # Architecture without a '/' and no subarchitecture:
            # assume 'generic'.
            altered_query_data['architecture'] += '/generic'

    if 'nodegroup' not in altered_query_data:
        # If 'nodegroup' is not explicitely specified, get the origin of the
        # request to figure out which nodegroup the new node should be
        # attached to.
        if request.data.get('autodetect_nodegroup', None) is None:
            # We insist on this to protect command-line API users who
            # are manually enlisting nodes.  You can't use the origin's
            # IP address to indicate in which nodegroup the new node belongs.
            raise ValidationError(
                "'autodetect_nodegroup' must be specified if 'nodegroup' "
                "parameter missing")
        nodegroup = find_nodegroup(request)
        if nodegroup is not None:
            altered_query_data['nodegroup'] = nodegroup

    Form = get_node_create_form(request.user)
    form = Form(data=altered_query_data)
    if form.is_valid():
        node = form.save()
        # Hack in the power parameters here.
        store_node_power_parameters(node, request)
        maaslog.info("%s: Enlisted new node", node.hostname)
        return node
    else:
        raise ValidationError(form.errors)