Example #1
0
    def on_get(self, req, resp, name):
        """
        Handles GET (or "status") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        key = '/commissaire/cluster/{0}/restart'.format(name)
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Restart GET requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return
        status, error = cherrypy.engine.publish('store-get', key)[0]
        self.logger.debug('Etcd Response: {0}'.format(status))
        if error:
            # Return "204 No Content" if we have no status,
            # meaning no restart is in progress.  The client
            # can't be expected to know that, so it's not a
            # client error (4xx).
            self.logger.debug((
                'Restart GET requested for {0} but no restart '
                'has ever been executed.').format(name))
            resp.status = falcon.HTTP_204
            return
        resp.status = falcon.HTTP_200
        req.context['model'] = ClusterRestart(**json.loads(status.value))
Example #2
0
    def on_get(self, req, resp, name):
        """
        Handles GET (or "status") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        key = '/commissaire/cluster/{0}/upgrade'.format(name)
        try:
            if not util.etcd_cluster_exists(self.store, name):
                self.logger.info(
                    'Upgrade GET requested for nonexistent cluster {0}'.format(
                        name))
                resp.status = falcon.HTTP_404
                return
            status = self.store.get(key)
            self.logger.debug('Etcd Response: {0}'.format(status))
        except etcd.EtcdKeyNotFound:
            # Return "204 No Content" if we have no status,
            # meaning no upgrade is in progress.  The client
            # can't be expected to know that, so it's not a
            # client error (4xx).
            self.logger.debug((
                'Upgrade GET requested for {0} but no upgrade '
                'has ever been executed.').format(name))
            resp.status = falcon.HTTP_204
            return

        resp.status = falcon.HTTP_200
        req.context['model'] = ClusterUpgrade(**json.loads(status.value))
Example #3
0
    def on_get(self, req, resp, name):
        """
        Handles GET (or "status") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Upgrade GET requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        try:
            cluster_upgrade = ClusterUpgrade.retrieve(name)
            self.logger.debug('Found ClusterUpgrade for {0}'.format(name))
        except:
            # Return "204 No Content" if we have no status,
            # meaning no upgrade is in progress.  The client
            # can't be expected to know that, so it's not a
            # client error (4xx).
            self.logger.debug((
                'Upgrade GET requested for {0} but no upgrade '
                'has ever been executed.').format(name))

            resp.status = falcon.HTTP_204
            return

        resp.status = falcon.HTTP_200
        req.context['model'] = cluster_upgrade
Example #4
0
    def on_put(self, req, resp, name):
        """
        Handles the creation of a new Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being created.
        :type name: str
        """
        # PUT is idempotent, and since there's no body to this request,
        # there's nothing to conflict with.  The request should always
        # succeed, even if we didn't actually do anything.
        if util.etcd_cluster_exists(self.store, name):
            self.logger.info(
                'Creation of already exisiting cluster {0} requested.'.format(
                    name))
        else:
            key = util.etcd_cluster_key(name)
            cluster = Cluster(status='ok', hostset=[])
            etcd_resp = self.store.set(key, cluster.to_json(secure=True))
            self.logger.info(
                'Created cluster {0} per request.'.format(name))
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
        resp.status = falcon.HTTP_201
Example #5
0
    def on_put(self, req, resp, name):
        """
        Handles the creation of a new Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being created.
        :type name: str
        """
        # PUT is idempotent, and since there's no body to this request,
        # there's nothing to conflict with.  The request should always
        # succeed, even if we didn't actually do anything.
        if util.etcd_cluster_exists(name):
            self.logger.info(
                'Creation of already exisiting cluster {0} requested.'.format(
                    name))
        else:
            key = util.etcd_cluster_key(name)
            cluster = Cluster(status='ok', hostset=[])
            etcd_resp, _ = cherrypy.engine.publish(
                'store-save', key, cluster.to_json(secure=True))[0]
            self.logger.info('Created cluster {0} per request.'.format(name))
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
        resp.status = falcon.HTTP_201
    def on_get(self, req, resp, name):
        """
        Handles GET (or "status") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Restart GET requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        try:
            store_manager = cherrypy.engine.publish('get-store-manager')[0]
            cluster_restart = store_manager.get(ClusterRestart.new(name=name))
        except:
            # Return "204 No Content" if we have no status,
            # meaning no restart is in progress.  The client
            # can't be expected to know that, so it's not a
            # client error (4xx).
            self.logger.debug((
                'Restart GET requested for {0} but no restart '
                'has ever been executed.').format(name))
            resp.status = falcon.HTTP_204
            return
        resp.status = falcon.HTTP_200
        req.context['model'] = cluster_restart
Example #7
0
    def on_get(self, req, resp, name):
        """
        Handles GET (or "status") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        key = '/commissaire/cluster/{0}/restart'.format(name)
        try:
            if not util.etcd_cluster_exists(self.store, name):
                self.logger.info(
                    'Restart GET requested for nonexistent cluster {0}'.format(
                        name))
                resp.status = falcon.HTTP_404
                return
            status = self.store.get(key)
            self.logger.debug('Etcd Response: {0}'.format(status))
        except etcd.EtcdKeyNotFound:
            # Return "204 No Content" if we have no status,
            # meaning no restart is in progress.  The client
            # can't be expected to know that, so it's not a
            # client error (4xx).
            self.logger.debug(('Restart GET requested for {0} but no restart '
                               'has ever been executed.').format(name))
            resp.status = falcon.HTTP_204
            return
        resp.status = falcon.HTTP_200
        req.context['model'] = ClusterRestart(**json.loads(status.value))
Example #8
0
    def on_get(self, req, resp, name):
        """
        Handles GET (or "status") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Restart GET requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        try:
            store_manager = cherrypy.engine.publish('get-store-manager')[0]
            cluster_restart = store_manager.get(ClusterRestart.new(name=name))
        except:
            # Return "204 No Content" if we have no status,
            # meaning no restart is in progress.  The client
            # can't be expected to know that, so it's not a
            # client error (4xx).
            self.logger.debug((
                'Restart GET requested for {0} but no restart '
                'has ever been executed.').format(name))
            resp.status = falcon.HTTP_204
            return
        resp.status = falcon.HTTP_200
        req.context['model'] = cluster_restart
Example #9
0
    def on_get(self, req, resp, name):
        """
        Handles GET (or "status") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        key = '/commissaire/cluster/{0}/upgrade'.format(name)
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Upgrade GET requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        status, error = cherrypy.engine.publish('store-get', key)[0]
        self.logger.debug('Etcd Response: {0}'.format(status))

        if error:
            # Return "204 No Content" if we have no status,
            # meaning no upgrade is in progress.  The client
            # can't be expected to know that, so it's not a
            # client error (4xx).
            self.logger.debug(('Upgrade GET requested for {0} but no upgrade '
                               'has ever been executed.').format(name))
            resp.status = falcon.HTTP_204
            return

        resp.status = falcon.HTTP_200
        req.context['model'] = ClusterUpgrade(**json.loads(status.value))
Example #10
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Upgrade PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress, return the current
        # status with response code 200 OK.
        try:
            store_manager = cherrypy.engine.publish('get-store-manager')[0]
            cluster_upgrade = store_manager.get(ClusterUpgrade.new(name=name))
            self.logger.debug('Found ClusterUpgrade for {0}'.format(name))
            if not cluster_upgrade.finished_at:
                self.logger.debug(
                    'Cluster {0} upgrade already in progress'.format(name))
                resp.status = falcon.HTTP_200
                req.context['model'] = cluster_upgrade
                return
        except:
            # This means one doesn't already exist.
            pass

        # TODO: Move to a poll?
        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        args = (store_manager.clone(), name, 'upgrade')
        p = Process(target=clusterexec, args=args)
        p.start()

        self.logger.debug('Started upgrade in clusterexecpool for {0}'.format(
            name))
        cluster_upgrade = ClusterUpgrade.new(
            name=name,
            status='in_process',
            started_at=datetime.datetime.utcnow().isoformat()
        )

        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        store_manager.save(cluster_upgrade)
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_upgrade
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Upgrade PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress, return the current
        # status with response code 200 OK.
        try:
            store_manager = cherrypy.engine.publish('get-store-manager')[0]
            cluster_upgrade = store_manager.get(ClusterUpgrade.new(name=name))
            self.logger.debug('Found ClusterUpgrade for {0}'.format(name))
            if not cluster_upgrade.finished_at:
                self.logger.debug(
                    'Cluster {0} upgrade already in progress'.format(name))
                resp.status = falcon.HTTP_200
                req.context['model'] = cluster_upgrade
                return
        except:
            # This means one doesn't already exist.
            pass

        # TODO: Move to a poll?
        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        args = (store_manager.clone(), name, 'upgrade')
        p = Process(target=clusterexec, args=args)
        p.start()

        self.logger.debug('Started upgrade in clusterexecpool for {0}'.format(
            name))
        cluster_upgrade = ClusterUpgrade.new(
            name=name,
            status='in_process',
            started_at=datetime.datetime.utcnow().isoformat()
        )

        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        store_manager.save(cluster_upgrade)
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_upgrade
Example #12
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Upgrade PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress, return the current
        # status with response code 200 OK.
        try:
            cluster_upgrade = ClusterUpgrade.retrieve(name)
            self.logger.debug('Found ClusterUpgrade for {0}'.format(name))
            if not cluster_upgrade.finished_at:
                self.logger.debug(
                    'Cluster {0} upgrade already in progress'.format(name))
                resp.status = falcon.HTTP_200
                req.context['model'] = cluster_upgrade
                return
        except:
            # This means one doesn't already exist.
            pass

        # TODO: Move to a poll?
        p = Process(target=clusterexec, args=(name, 'upgrade'))
        p.start()

        self.logger.debug('Started upgrade in clusterexecpool for {0}'.format(
            name))
        cluster_upgrade_default = {
            'status': 'in_process',
            'upgraded': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_upgrade = ClusterUpgrade(**cluster_upgrade_default)
        cluster_upgrade.save(name)
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_upgrade
Example #13
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Restart PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress, return the current
        # status with response code 200 OK.
        key = '/commissaire/cluster/{0}/restart'.format(name)
        etcd_resp, error = cherrypy.engine.publish('store-get', key)[0]
        if not error:
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
            cluster_restart = ClusterRestart(**json.loads(etcd_resp.value))
            if not cluster_restart.finished_at:
                self.logger.debug(
                    'Cluster {0} restart already in progress'.format(name))
                resp.status = falcon.HTTP_200
                req.context['model'] = cluster_restart
                return

        # TODO: Move to a poll?
        p = Process(target=clusterexec, args=(name, 'restart'))
        p.start()

        self.logger.debug('Started restart in clusterexecpool for {0}'.format(
            name))
        cluster_restart_default = {
            'status': 'in_process',
            'restarted': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_restart = ClusterRestart(**cluster_restart_default)
        cherrypy.engine.publish('store-save', key, cluster_restart.to_json())
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_restart
Example #14
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Restart PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress, return the current
        # status with response code 200 OK.
        key = '/commissaire/cluster/{0}/restart'.format(name)
        etcd_resp, error = cherrypy.engine.publish('store-get', key)[0]
        if not error:
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
            cluster_restart = ClusterRestart(**json.loads(etcd_resp.value))
            if not cluster_restart.finished_at:
                self.logger.debug(
                    'Cluster {0} restart already in progress'.format(name))
                resp.status = falcon.HTTP_200
                req.context['model'] = cluster_restart
                return

        # TODO: Move to a poll?
        p = Process(target=clusterexec, args=(name, 'restart'))
        p.start()

        self.logger.debug(
            'Started restart in clusterexecpool for {0}'.format(name))
        cluster_restart_default = {
            'status': 'in_process',
            'restarted': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_restart = ClusterRestart(**cluster_restart_default)
        cherrypy.engine.publish('store-save', key, cluster_restart.to_json())
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_restart
Example #15
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(self.store, name):
            self.logger.info(
                'Restart PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress, return the current
        # status with response code 200 OK.
        key = '/commissaire/cluster/{0}/restart'.format(name)
        try:
            etcd_resp = self.store.get(key)
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
            cluster_restart = ClusterRestart(**json.loads(etcd_resp.value))
            if not cluster_restart.finished_at:
                self.logger.debug(
                    'Cluster {0} restart already in progress'.format(name))
                resp.status = falcon.HTTP_200
                req.context['model'] = cluster_restart
                return
        except etcd.EtcdKeyNotFound:
            pass

        POOLS['clusterexecpool'].spawn(clusterexec.clusterexec, name,
                                       'restart', self.store)
        self.logger.debug(
            'Started restart in clusterexecpool for {0}'.format(name))
        cluster_restart_default = {
            'status': 'in_process',
            'restarted': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_restart = ClusterRestart(**cluster_restart_default)
        self.store.set(key, cluster_restart.to_json())
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_restart
Example #16
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster restart.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being restarted.
        :type name: str
        """
        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(self.store, name):
            self.logger.info(
                'Restart PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress, return the current
        # status with response code 200 OK.
        key = '/commissaire/cluster/{0}/restart'.format(name)
        try:
            etcd_resp = self.store.get(key)
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
            cluster_restart = ClusterRestart(**json.loads(etcd_resp.value))
            if not cluster_restart.finished_at:
                self.logger.debug(
                    'Cluster {0} restart already in progress'.format(name))
                resp.status = falcon.HTTP_200
                req.context['model'] = cluster_restart
                return
        except etcd.EtcdKeyNotFound:
            pass

        POOLS['clusterexecpool'].spawn(
            clusterexec.clusterexec, name, 'restart', self.store)
        self.logger.debug('Started restart in clusterexecpool for {0}'.format(
            name))
        cluster_restart_default = {
            'status': 'in_process',
            'restarted': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_restart = ClusterRestart(**cluster_restart_default)
        self.store.set(key, cluster_restart.to_json())
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_restart
Example #17
0
    def on_delete(self, req, resp, name):
        """
        Handles the deletion of a Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being deleted.
        :type name: str
        """
        resp.body = '{}'
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Deleting for non-existent cluster {0} requested.'.format(
                    name))
            resp.status = falcon.HTTP_404
        else:
            self.store.delete(util.etcd_cluster_key(name))
            resp.status = falcon.HTTP_200
            self.logger.info('Deleted cluster {0} per request.'.format(name))
Example #18
0
    def on_delete(self, req, resp, name):
        """
        Handles the deletion of a Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being deleted.
        :type name: str
        """
        resp.body = '{}'
        if not util.etcd_cluster_exists(self.store, name):
            self.logger.info(
                'Deleting for non-existent cluster {0} requested.'.format(
                    name))
            resp.status = falcon.HTTP_404
        else:
            self.store.delete(util.etcd_cluster_key(name))
            resp.status = falcon.HTTP_410
            self.logger.info(
                'Deleted cluster {0} per request.'.format(name))
Example #19
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        data = req.stream.read().decode()
        try:
            args = json.loads(data)
            upgrade_to = args['upgrade_to']
        except (KeyError, ValueError):
            resp.status = falcon.HTTP_400
            return

        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Upgrade PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress and the requested version
        # matches, return the current status with response code 200 OK.
        # If the requested version conflicts with the operation in progress,
        # return the current status with response code 409 Conflict.
        key = '/commissaire/cluster/{0}/upgrade'.format(name)
        etcd_resp, error = cherrypy.engine.publish('store-get', key)[0]
        self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
        if not error:
            cluster_upgrade = ClusterUpgrade(**json.loads(etcd_resp.value))
            if not cluster_upgrade.finished_at:
                if cluster_upgrade.upgrade_to == upgrade_to:
                    self.logger.debug(
                        'Cluster {0} upgrade to {1} already in progress'.
                        format(name, upgrade_to))
                    resp.status = falcon.HTTP_200
                else:
                    self.logger.debug(
                        'Cluster upgrade to {0} requested while '
                        'upgrade to {1} was already in progress'.format(
                            upgrade_to, cluster_upgrade.upgrade_to))
                    resp.status = falcon.HTTP_409
                req.context['model'] = cluster_upgrade
                return

        # FIXME: How do I pass 'upgrade_to'?
        p = Process(target=clusterexec, args=(name, 'upgrade'))
        p.start()
        self.logger.debug(
            'Started upgrade in clusterexecpool for {0}'.format(name))
        cluster_upgrade_default = {
            'status': 'in_process',
            'upgrade_to': upgrade_to,
            'upgraded': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_upgrade = ClusterUpgrade(**cluster_upgrade_default)
        cherrypy.engine.publish('store-save', key, cluster_upgrade.to_json())
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_upgrade
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a tree image deployment
        across a Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster undergoing deployment.
        :type name: str
        """
        data = req.stream.read().decode()
        try:
            args = json.loads(data)
            version = args['version']
        except (KeyError, ValueError):
            resp.status = falcon.HTTP_400
            return

        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Deploy PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        # If the operation is already in progress and the requested version
        # matches, return the current status with response code 200 OK.
        # If the requested version conflicts with the operation in progress,
        # return the current status with response code 409 Conflict.
        try:
            cluster_deploy = store_manager.get(ClusterDeploy.new(name=name))
            self.logger.debug('Found ClusterDeploy for {0}'.format(name))
            if not cluster_deploy.finished_at:
                if cluster_deploy.version == version:
                    self.logger.debug(
                        'Cluster {0} deployment to {1} already in progress'.
                        format(name, version))
                    resp.status = falcon.HTTP_200
                else:
                    self.logger.debug(
                        'Cluster deployment to {0} requested while '
                        'deployment to {1} was already in progress'.
                        format(version, cluster_deploy.version))
                    resp.status = falcon.HTTP_409
                req.context['model'] = cluster_deploy
                return
        except:
            pass

        args = (store_manager.clone(), name, 'deploy', {'version': version})
        p = Process(target=clusterexec, args=args)
        p.start()
        self.logger.debug(
            'Started deployment to {0} in clusterexecpool for {1}'.format(
                version, name))
        cluster_deploy = ClusterDeploy.new(
            name=name,
            status='in_process',
            started_at=datetime.datetime.utcnow().isoformat()
        )

        store_manager.save(cluster_deploy)
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_deploy
Example #21
0
    def on_put(self, req, resp, address):
        """
        Handles the creation of a new Host.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param address: The address of the Host being requested.
        :type address: str
        """
        try:
            # Extract what we need from the input data.
            # Don't treat it as a skeletal host record.
            req_data = req.stream.read()
            req_body = json.loads(req_data.decode())
            ssh_priv_key = req_body['ssh_priv_key']
            # Cluster member is optional.
            cluster_name = req_body.get('cluster', None)
        except (KeyError, ValueError):
            self.logger.info(
                'Bad client PUT request for host {0}: {1}'.
                format(address, req_data))
            resp.status = falcon.HTTP_400
            return

        key = util.etcd_host_key(address)
        try:
            etcd_resp = self.store.get(key)
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))

            # Check if the request conflicts with the existing host.
            existing_host = Host(**json.loads(etcd_resp.value))
            if existing_host.ssh_priv_key != ssh_priv_key:
                resp.status = falcon.HTTP_409
                return
            if cluster_name:
                try:
                    assert util.etcd_cluster_has_host(
                        self.store, cluster_name, address)
                except (AssertionError, KeyError):
                    resp.status = falcon.HTTP_409
                    return

            # Request is compatible with the existing host, so
            # we're done.  (Not using HTTP_201 since we didn't
            # actually create anything.)
            resp.status = falcon.HTTP_200
            req.context['model'] = existing_host
            return
        except etcd.EtcdKeyNotFound:
            pass

        host_creation = {
            'address': address,
            'ssh_priv_key': ssh_priv_key,
            'os': '',
            'status': 'investigating',
            'cpus': -1,
            'memory': -1,
            'space': -1,
            'last_check': None
        }

        # Verify the cluster exists, if given.  Do it now
        # so we can fail before writing anything to etcd.
        if cluster_name:
            if not util.etcd_cluster_exists(self.store, cluster_name):
                resp.status = falcon.HTTP_409
                return

        host = Host(**host_creation)
        new_host = self.store.set(key, host.to_json(secure=True))
        INVESTIGATE_QUEUE.put((host_creation, ssh_priv_key))

        # Add host to the requested cluster.
        if cluster_name:
            util.etcd_cluster_add_host(self.store, cluster_name, address)

        resp.status = falcon.HTTP_201
        req.context['model'] = Host(**json.loads(new_host.value))
Example #22
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a tree image deployment
        across a Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster undergoing deployment.
        :type name: str
        """
        data = req.stream.read().decode()
        try:
            args = json.loads(data)
            version = args['version']
        except (KeyError, ValueError):
            resp.status = falcon.HTTP_400
            return

        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Deploy PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        store_manager = cherrypy.engine.publish('get-store-manager')[0]
        # If the operation is already in progress and the requested version
        # matches, return the current status with response code 200 OK.
        # If the requested version conflicts with the operation in progress,
        # return the current status with response code 409 Conflict.
        try:
            cluster_deploy = store_manager.get(ClusterDeploy.new(name=name))
            self.logger.debug('Found ClusterDeploy for {0}'.format(name))
            if not cluster_deploy.finished_at:
                if cluster_deploy.version == version:
                    self.logger.debug(
                        'Cluster {0} deployment to {1} already in progress'.
                        format(name, version))
                    resp.status = falcon.HTTP_200
                else:
                    self.logger.debug(
                        'Cluster deployment to {0} requested while '
                        'deployment to {1} was already in progress'.
                        format(version, cluster_deploy.version))
                    resp.status = falcon.HTTP_409
                req.context['model'] = cluster_deploy
                return
        except:
            pass

        args = (store_manager.clone(), name, 'deploy', {'version': version})
        p = Process(target=clusterexec, args=args)
        p.start()
        self.logger.debug(
            'Started deployment to {0} in clusterexecpool for {1}'.format(
                version, name))
        cluster_deploy = ClusterDeploy.new(
            name=name,
            status='in_process',
            started_at=datetime.datetime.utcnow().isoformat()
        )

        store_manager.save(cluster_deploy)
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_deploy
Example #23
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a Cluster upgrade.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being upgraded.
        :type name: str
        """
        data = req.stream.read().decode()
        try:
            args = json.loads(data)
            upgrade_to = args['upgrade_to']
        except (KeyError, ValueError):
            resp.status = falcon.HTTP_400
            return

        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(self.store, name):
            self.logger.info(
                'Upgrade PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress and the requested version
        # matches, return the current status with response code 200 OK.
        # If the requested version conflicts with the operation in progress,
        # return the current status with response code 409 Conflict.
        key = '/commissaire/cluster/{0}/upgrade'.format(name)
        try:
            etcd_resp = self.store.get(key)
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))
            cluster_upgrade = ClusterUpgrade(**json.loads(etcd_resp.value))
            if not cluster_upgrade.finished_at:
                if cluster_upgrade.upgrade_to == upgrade_to:
                    self.logger.debug(
                        'Cluster {0} upgrade to {1} already in progress'.
                        format(name, upgrade_to))
                    resp.status = falcon.HTTP_200
                else:
                    self.logger.debug(
                        'Cluster upgrade to {0} requested while '
                        'upgrade to {1} was already in progress'.
                        format(upgrade_to, cluster_upgrade.upgrade_to))
                    resp.status = falcon.HTTP_409
                req.context['model'] = cluster_upgrade
                return
        except etcd.EtcdKeyNotFound:
            pass

        # FIXME: How do I pass 'upgrade_to'?
        POOLS['clusterexecpool'].spawn(
            clusterexec.clusterexec, name, 'upgrade', self.store)
        self.logger.debug('Started upgrade in clusterexecpool for {0}'.format(
            name))
        cluster_upgrade_default = {
            'status': 'in_process',
            'upgrade_to': upgrade_to,
            'upgraded': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_upgrade = ClusterUpgrade(**cluster_upgrade_default)
        self.store.set(key, cluster_upgrade.to_json())
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_upgrade
Example #24
0
    def on_put(self, req, resp, name):
        """
        Handles PUT (or "initiate") requests for a tree image deployment
        across a Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster undergoing deployment.
        :type name: str
        """
        data = req.stream.read().decode()
        try:
            args = json.loads(data)
            version = args['version']
        except (KeyError, ValueError):
            resp.status = falcon.HTTP_400
            return

        # Make sure the cluster name is valid.
        if not util.etcd_cluster_exists(name):
            self.logger.info(
                'Upgrade PUT requested for nonexistent cluster {0}'.format(
                    name))
            resp.status = falcon.HTTP_404
            return

        # If the operation is already in progress and the requested version
        # matches, return the current status with response code 200 OK.
        # If the requested version conflicts with the operation in progress,
        # return the current status with response code 409 Conflict.
        try:
            cluster_deploy = ClusterDeploy.retrieve(name)
            self.logger.debug('Found ClusterDeploy for {0}'.format(name))
            if not cluster_deploy.finished_at:
                if cluster_deploy.version == version:
                    self.logger.debug(
                        'Cluster {0} deployment to {1} already in progress'.
                        format(name, version))
                    resp.status = falcon.HTTP_200
                else:
                    self.logger.debug(
                        'Cluster deployment to {0} requested while '
                        'upgrade to {1} was already in progress'.
                        format(version, cluster_deploy.version))
                    resp.status = falcon.HTTP_409
                req.context['model'] = cluster_deploy
                return
        except:
            pass

        p = Process(target=clusterexec,
                    args=(name, 'upgrade', {'version': version}))
        p.start()
        self.logger.debug('Started upgrade in clusterexecpool for {0}'.format(
            name))
        cluster_deploy_default = {
            'status': 'in_process',
            'version': version,
            'deployed': [],
            'in_process': [],
            'started_at': datetime.datetime.utcnow().isoformat(),
            'finished_at': None
        }
        cluster_deploy = ClusterDeploy(**cluster_deploy_default)
        cluster_deploy.save(name)
        resp.status = falcon.HTTP_201
        req.context['model'] = cluster_deploy