コード例 #1
0
ファイル: provisioning.py プロジェクト: ParadropLabs/Paradrop
def provision_self(update_manager):
    """
    Provision the node.

    Returns a deferred.
    """
    name = "node-{:x}".format(devices.get_hardware_serial())

    conf = read_provisioning_conf()
    batch_id = conf.get("batch_id", None)
    batch_key = conf.get("batch_key", None)

    def cbresponse(response):
        router = response.data['router']
        nexus.core.provision(router['_id'])
        nexus.core.saveKey(router['password'], 'apitoken')
        nexus.core.jwt_valid = True

        batch = response.data['batch']
        hostconfig_patch = batch.get("hostconfig_patch", [])
        zerotier_networks = batch.get("zerotier_networks", [])
        update_manager.add_provision_update(hostconfig_patch, zerotier_networks)

        write_provisioning_result(response.data)

    data = {
        "name": name,
        "key": batch_key,
        "zerotier_address": zerotier.getAddress()
    }

    request = PDServerRequest('/api/batches/{}/provision'.format(batch_id))
    d = request.post(**data)
    d.addCallback(cbresponse)
    return d
コード例 #2
0
    def send(self, report):
        request = PDServerRequest('/api/routers/{router_id}/' + self.model)
        d = request.post(**report)

        # Check for error code and retry.
        def cbresponse(response):
            if not response.success:
                out.warn('{} to {} returned code {}'.format(
                    request.method, request.url, response.code))
                if self.max_retries is None or self.retries < self.max_retries:
                    reactor.callLater(self.retryDelay, self.send, report)
                    self.retries += 1
                    self.increaseDelay()
                nexus.core.jwt_valid = False
            else:
                nexus.core.jwt_valid = True

        # Check for connection failures and retry.
        def cberror(ignored):
            out.warn('{} to {} failed'.format(request.method, request.url))
            if self.max_retries is None or self.retries < self.max_retries:
                reactor.callLater(self.retryDelay, self.send, report)
                self.retries += 1
                self.increaseDelay()
            nexus.core.jwt_valid = False

        d.addCallback(cbresponse)
        d.addErrback(cberror)
        return d
コード例 #3
0
    def pull_update(self, _auto=False):
        """
        Start updates by polling the server for the latest updates.

        This is the only method that needs to be called from outside.  The rest
        are triggered asynchronously.

        Call chain:
        pull_update -> _updates_received -> _update_complete

        _auto: Set to True when called by the scheduled LoopingCall.
        """
        # If this call was triggered externally (_auto=False), then reset the
        # timer for the next scheduled call so we do not poll again too soon.
        if not _auto and self.scheduled_call.running:
            self.scheduled_call.reset()

        def handle_error(error):
            print("Request for updates failed: {}".format(error.getErrorMessage()))

        request = PDServerRequest('/api/routers/{router_id}/updates')
        d = request.get(completed=False)
        d.addCallback(self._updates_received)
        d.addErrback(handle_error)
        yield d
コード例 #4
0
    def _update_complete(self, update):
        """
        Internal: callback after an update operation has been completed
        (successfuly or otherwise) and send a notification to the server.
        """
        # If delegated to an external program, we do not report to pdserver
        # that the update is complete.
        if update.delegated:
            return

        out.info("The update is done, report it to server...")
        update_id = update.external['update_id']
        success = update.result.get('success', False)

        request = PDServerRequest('/api/routers/{router_id}/updates/' +
                                  str(update_id))
        d = request.patch(
            {
                'op': 'replace',
                'path': '/completed',
                'value': True
            }, {
                'op': 'replace',
                'path': '/success',
                'value': success
            })

        return d
コード例 #5
0
    def provision(self, request):
        """
        Provision the device with credentials from a cloud controller.
        """
        cors.config_cors(request)
        body = json.loads(request.content.read().decode('utf-8'))
        routerId = body['routerId']
        apitoken = body['apitoken']
        pdserver = body['pdserver']
        wampRouter = body['wampRouter']

        changed = False
        if routerId != nexus.core.info.pdid \
            or pdserver != nexus.core.info.pdserver \
            or wampRouter != nexus.core.info.wampRouter:
            if pdserver and wampRouter:
                nexus.core.provision(routerId, pdserver, wampRouter)
            else:
                nexus.core.provision(routerId)
            changed = True

        if apitoken != nexus.core.getKey('apitoken'):
            nexus.core.saveKey(apitoken, 'apitoken')
            changed = True

        if changed:
            PDServerRequest.resetToken()
            nexus.core.jwt_valid = False

            def set_update_fetcher(session):
                session.set_update_fetcher(self.update_fetcher)

            @inlineCallbacks
            def start_polling(result):
                yield self.update_fetcher.start_polling()

            def send_response(result):
                response = dict()
                response['provisioned'] = True
                response['httpConnected'] = nexus.core.jwt_valid
                response['wampConnected'] = nexus.core.wamp_connected
                request.setHeader('Content-Type', 'application/json')
                return json.dumps(response)

            wampDeferred = nexus.core.connect(WampSession)
            wampDeferred.addCallback(set_update_fetcher)

            httpDeferred = sendStateReport()
            httpDeferred.addCallback(start_polling)

            identDeferred = sendNodeIdentity()

            dl = DeferredList([wampDeferred, httpDeferred, identDeferred],
                    consumeErrors=True)
            dl.addBoth(send_response)
            reactor.callLater(6, dl.cancel)
            return dl
        else:
            return json.dumps({'success': False,
                               'message': 'No change on the provision parameters'})
コード例 #6
0
ファイル: reporting.py プロジェクト: ParadropLabs/Paradrop
    def send(self, report):
        request = PDServerRequest('/api/routers/{router_id}')
        d = request.patch(*report)

        # Check for error code and retry.
        def cbresponse(response):
            if not response.success:
                out.warn('{} to {} returned code {}'.format(request.method,
                    request.url, response.code))
                if self.max_retries is None or self.retries < self.max_retries:
                    reactor.callLater(self.retryDelay, self.send, report)
                    self.retries += 1
                    self.increaseDelay()
                nexus.core.jwt_valid = False
            else:
                nexus.core.jwt_valid = True

        # Check for connection failures and retry.
        def cberror(ignored):
            out.warn('{} to {} failed'.format(request.method, request.url))
            if self.max_retries is None or self.retries < self.max_retries:
                reactor.callLater(self.retryDelay, self.send, report)
                self.retries += 1
                self.increaseDelay()
            nexus.core.jwt_valid = False

        d.addCallback(cbresponse)
        d.addErrback(cberror)
        return d
コード例 #7
0
def verify_cloud_token(token):
    headers = {"Authorization": "Bearer {}".format(token)}

    # Pass custom Authorization header and setAuthHeader=False to prevent
    # PDServerRequest from using the node's own authorization token.
    request = PDServerRequest("/api/users/me",
                              headers=headers,
                              setAuthHeader=False)
    return request.get()
コード例 #8
0
    def auth_cloud(self, request):
        """
        Login using credentials from the cloud controller.

        This is an experimental new login method that lets users
        present a token that they received from the cloud controller
        as a login credential for a node. The idea is to enable easy
        access for multiple developers to share a node, for example,
        during a tutorial.

        Instead of a username/password, the user presents a token received
        from the cloud controller. The verify_cloud_token function
        verifies the validity of the token with the controller, and if
        successful, retrieves information about the bearer, particularly
        the username and role. Finally, we generate a new token that
        enables the user to authenticate with local API endpoints.
        """
        cors.config_cors(request)
        request.setHeader('Content-Type', 'application/json')

        body = json.loads(request.content.read())
        token = body.get('token', '')

        headers = {"Authorization": "Bearer {}".format(token)}
        node_id = nexus.core.info.pdid

        # Pass custom Authorization header and setAuthHeader=False to prevent
        # PDServerRequest from using the node's own authorization token.
        d1 = PDServerRequest("/api/users/me",
                             headers=headers,
                             setAuthHeader=False).get()
        d2 = PDServerRequest("/api/routers/{}".format(node_id),
                             headers=headers,
                             setAuthHeader=False).get()

        def token_verified(responses):
            for response in responses:
                if not response.success or response.data is None:
                    request.setResponseCode(401)
                    return

            remote_user = responses[0].data
            node_info = responses[1].data

            role = get_access_level(remote_user, node_info)

            token = self.token_manager.issue(remote_user['email'],
                                             domain="paradrop.org",
                                             role=role)

            result = {'token': token, 'username': remote_user['email']}
            return json.dumps(result)

        d = defer.gatherResults([d1, d2])
        d.addCallback(token_verified)
        return d
コード例 #9
0
ファイル: auth.py プロジェクト: ParadropLabs/Paradrop
def verify_cloud_token(token):
    headers = {
        "Authorization": "Bearer {}".format(token)
    }

    # Pass custom Authorization header and setAuthHeader=False to prevent
    # PDServerRequest from using the node's own authorization token.
    request = PDServerRequest("/api/users/me", headers=headers,
            setAuthHeader=False)
    return request.get()
コード例 #10
0
 def _update_ignored(self, update):
     """
     Internal: callback for an update that we are ignoring because
     it was started previously and never completed.
     """
     update_id = update['_id']
     request = PDServerRequest('/api/routers/{router_id}/updates/' +
             str(update_id))
     d = request.patch(
         {'op': 'replace', 'path': '/completed', 'value': True},
         {'op': 'replace', 'path': '/success', 'value': False}
     )
     return d
コード例 #11
0
    def start_long_poll(self):
        self.long_poll_started = True

        while self.use_long_poll:
            request = PDServerRequest('/api/routers/{router_id}/updates/poll')
            try:
                response = yield request.get()
            except Exception:
                pass
            else:
                # 200 = update(s) available
                # 204 = no updates yet
                # 404 = server does not support this endpoint
                if response.code == 200:
                    self.pull_update(_auto=False)
                elif response.code == 404:
                    self.use_long_poll = False
コード例 #12
0
    def progress(self, message):
        if self.pkg is not None:
            self.pkg.request.write(message + '\n')

        # TODO Look into this.
        # This might happen during router initialization.  If nexus.core is
        # None, we do not know the router's identity, so we cannot publish any
        # messages.
        if nexus.core is None:
            return

        data = {'time': time.time(), 'message': message}

        def handleError(error):
            print("Error sending message: {}".format(error.getErrorMessage()))

        # The external field is set for updates from pdserver but not for
        # locally-initiated (sideloaded) updates.
        update_id = None
        if hasattr(self, 'external'):
            update_id = self.external['update_id']
            request = PDServerRequest(
                '/api/routers/{}/updates/{}/messages'.format(
                    nexus.core.info.pdid, update_id))
            d = request.post(**data)
            d.addErrback(handleError)

        session = getattr(nexus.core, 'session', None)
        if session is not None:
            data['update_id'] = update_id

            # Catch the occasional Exception due to connectivity failure.  We
            # don't want to fail a chute installation just because we had problems
            # sending the log messages.
            try:
                session.publish(session.uriPrefix + 'updateProgress', data)
            except Exception as error:
                out.warn("Publish failed: {} {}".format(
                    error.__class__, error))

        # Send messages to internal consumers (e.g. open websocket connections)
        self.messages.append(data)
        for observer in self.message_observers:
            observer.on_message(data)
コード例 #13
0
    def started(self):
        """
        This function should be called when the updated object is dequeued and
        execution is about to begin.

        Sends a notification to the pdserver if this is a tracked update.
        """
        # TODO Look into this.
        # This might happen during router initialization.  If nexus.core is
        # None, we do not know the router's identity, so we cannot publish any
        # messages.
        if nexus.core is None:
            return

        # The external field is set for updates from pdserver but not for
        # locally-initiated (sideloaded) updates.
        if hasattr(self, 'external'):
            update_id = self.external['update_id']
            request = PDServerRequest('/api/routers/{router_id}/updates/' +
                                      str(update_id))
            request.patch({'op': 'replace', 'path': '/started', 'value': True})
コード例 #14
0
def provision_self(update_manager):
    """
    Provision the node.

    Returns a deferred.
    """
    name = "node-{:x}".format(devices.get_hardware_serial())

    conf = read_provisioning_conf()
    batch_id = conf.get("batch_id", None)
    batch_key = conf.get("batch_key", None)

    def cbresponse(response):
        router = response.data['router']
        nexus.core.provision(router['_id'])
        nexus.core.saveKey(router['password'], 'apitoken')
        nexus.core.jwt_valid = True

        batch = response.data['batch']
        hostconfig_patch = batch.get("hostconfig_patch", [])
        zerotier_networks = batch.get("zerotier_networks", [])
        update_manager.add_provision_update(hostconfig_patch,
                                            zerotier_networks)

        write_provisioning_result(response.data)

    data = {
        "name": name,
        "key": batch_key,
        "zerotier_address": zerotier.getAddress()
    }

    request = PDServerRequest('/api/batches/{}/provision'.format(batch_id))
    d = request.post(**data)
    d.addCallback(cbresponse)
    return d