def get_instance_state(self, instance_id):
     try:
         response = tornado_requests.request("GET",
                                     "http://%s:%s/v2/instances/%s"%(tenant_templ.cloudverifier_ip,tenant_templ.cloudverifier_port,instance_id),context=tenant_templ.context)
     except Exception as e:
         logger.error("Status command response: %s:%s Unexpected response from Cloud Verifier."%(tenant_templ.cloudverifier_ip,tenant_templ.cloudverifier_port))
         logger.error(traceback.print_exc())
         logger.error("Error: %s "%str(e))
         raise e
     
     inst_response_body = response.json()
     
     if response.status_code != 200 and response.status_code != 404:
         logger.error("Status command response: %d Unexpected response from Cloud Verifier."%response.status_code)
         common.log_http_response(logger,logging.ERROR,inst_response_body)
         return None
     
     if "results" not in inst_response_body:
         logger.critical("Error: unexpected http response body from Cloud Verifier: %s"%str(response.status_code))
         return None 
     
     # Node not added to CV (but still registered) 
     if response.status_code == 404:
         return {"operational_state" : cloud_verifier_common.CloudInstance_Operational_State.REGISTERED}
     else:
         return inst_response_body["results"]
     
     return None
def getKeys(registrar_ip,registrar_port,instance_id):
    global context
    
    #make absolutely sure you don't ask for AIKs unauthenticated
    if context is None or context.verify_mode != ssl.CERT_REQUIRED:
        raise Exception("It is unsafe to use this interface to query AIKs with out server authenticated TLS")
    
    try:
        response = tornado_requests.request("GET",
                                            "http://%s:%s/v2/instances/%s"%(registrar_ip,registrar_port,instance_id),
                                            context=context)
        
        response_body = response.json()
        
        if response.status_code != 200:
            logger.critical("Error: unexpected http response code from Registrar Server: %s"%str(response.status_code))
            common.log_http_response(logger,logging.CRITICAL,response_body)
            return None 
        
        if "results" not in response_body:
            logger.critical("Error: unexpected http response body from Registrar Server: %s"%str(response.status_code))
            return None 
        
        if "aik" not in response_body["results"]:
            logger.critical("Error: did not receive aik from Registrar Server: %s"%str(response.status_code))
            return None 
        
        return response_body["results"]
    except Exception as e:
        logger.critical(traceback.format_exc())
        logger.critical("An unexpected error occurred: " + str(e))
        
    return None
Example #3
0
 def do_cvdelete(self):        
     response = tornado_requests.request("DELETE","http://%s:%s/v2/instances/%s"%(self.cloudverifier_ip,self.cloudverifier_port,self.node_uuid),context=self.context)
     if response.status_code != 200:
         logger.error("Delete command response: %d Unexpected response from Cloud Verifier."%response.status_code)
         common.log_http_response(logger,logging.ERROR,response.json())
     else:
         logger.info("Node %s deleted from CV"%(self.node_uuid))
Example #4
0
 def do_cvdelete(self):
     response = tornado_requests.request(
         "DELETE",
         "http://%s:%s/agents/%s" %
         (self.cloudverifier_ip, self.cloudverifier_port, self.agent_uuid),
         context=self.context)
     if response.status_code == 202:
         deleted = False
         for _ in range(12):
             response = tornado_requests.request(
                 "GET",
                 "http://%s:%s/agents/%s" %
                 (self.cloudverifier_ip, self.cloudverifier_port,
                  self.agent_uuid),
                 context=self.context)
             if response.status_code == 404:
                 deleted = True
                 break
             time.sleep(.4)
         if deleted:
             logger.info("CV completed deletion of agent %s" %
                         (self.agent_uuid))
         else:
             raise UserError(
                 "Timed out waiting for delete of agent %s to complete at CV"
                 % self.agent_uuid)
     elif response.status_code == 200:
         logger.info("Agent %s deleted from the CV" % (self.agent_uuid))
     else:
         #raise UserError("Delete command response: %d Unexpected response from Cloud Verifier."%response.status_code)
         common.log_http_response(logger, logging.ERROR, response.json())
Example #5
0
    def do_cv(self):
        """initiaite v, instance_id and ip
        initiate the cloudinit sequence"""
        b64_v = base64.b64encode(self.V)
        logger.debug("b64_v:" + b64_v)
        data = {
            'v': b64_v,
            'cloudnode_ip': self.cv_cloudnode_ip,
            'cloudnode_port': self.cloudnode_port,
            'tpm_policy': json.dumps(self.tpm_policy),
            'vtpm_policy': json.dumps(self.vtpm_policy),
            'ima_whitelist': json.dumps(self.ima_whitelist),
            'metadata': json.dumps(self.metadata),
            'revocation_key': self.revocation_key,
        }

        json_message = json.dumps(data)
        response = tornado_requests.request(
            "POST",
            "http://%s:%s/v2/instances/%s" %
            (self.cloudverifier_ip, self.cloudverifier_port, self.node_uuid),
            data=json_message,
            context=self.context)
        if response.status_code == 409:
            # this is a conflict, delete first then re-add
            logger.warning(
                "Node already existed at CV.  Deleting and re-adding...")
            self.do_cvdelete()
            self.do_cv()
        elif response.status_code != 200:
            common.log_http_response(logger, logging.ERROR, response.json())
            raise Exception(
                "POST command response: %d Unexpected response from Cloud Verifier: %s"
                % (response.status_code, response.body))
Example #6
0
 def do_cvstop(self):
     response = tornado_requests.request("PUT","http://%s:%s/agents/%s/stop"%(self.cloudverifier_ip,self.cloudverifier_port,self.agent_uuid),context=self.context,data=b'')
     if response.status_code != 200:
         raise UserError("Update command response: %d Unexpected response from Cloud Verifier."%response.status_code)
         common.log_http_response(logger,logging.ERROR,response.json())
     else:
         logger.info("Agent %s stopped"%(self.agent_uuid))
Example #7
0
 def do_cv(self):
     """initiaite v, agent_id and ip
     initiate the cloudinit sequence"""
     b64_v = base64.b64encode(self.V)
     logger.debug("b64_v:" + b64_v)
     data = {
         'v': b64_v,
         'cloudagent_ip': self.cv_cloudagent_ip,
         'cloudagent_port': self.cloudagent_port,
         'tpm_policy': json.dumps(self.tpm_policy),
         'vtpm_policy':json.dumps(self.vtpm_policy),
         'ima_whitelist':json.dumps(self.ima_whitelist),
         'metadata':json.dumps(self.metadata),
         'revocation_key':self.revocation_key,
         'accept_tpm_hash_algs':self.accept_tpm_hash_algs,
         'accept_tpm_encryption_algs':self.accept_tpm_encryption_algs,
         'accept_tpm_signing_algs':self.accept_tpm_signing_algs,
     }
     
     json_message = json.dumps(data)
     response = tornado_requests.request("POST","http://%s:%s/agents/%s"%(self.cloudverifier_ip,self.cloudverifier_port,self.agent_uuid),data=json_message,context=self.context)
     if response.status_code == 409:
         # this is a conflict, need to update or delete it
         raise UserError("Agent %s already existed at CV.  Please use delete or update."%self.agent_uuid)
     elif response.status_code != 200:
         common.log_http_response(logger,logging.ERROR,response.json())
         raise UserError("POST command response: %d Unexpected response from Cloud Verifier: %s"%(response.status_code,response.body))
def doRegisterNode(registrar_ip,registrar_port,instance_id,pub_ek,ekcert,pub_aik):
    data = {
    'ek': pub_ek,
    'ekcert': ekcert,
    'aik': pub_aik,
    }
    v_json_message = json.dumps(data)
    
    response = tornado_requests.request("POST",
                                        "http://%s:%s/v2/instances/%s"%(registrar_ip,registrar_port,instance_id),
                                        data=v_json_message,
                                        context=None)

    response_body = response.json() 
    
    if response.status_code != 200:
        logger.error("Error: unexpected http response code from Registrar Server: " + str(response.status_code))
        common.log_http_response(logger,logging.ERROR,response_body)
        return None
    
    logger.info("Node registration requested for %s"%instance_id)
    
    if "results" not in response_body:
        logger.critical("Error: unexpected http response body from Registrar Server: %s"%str(response.status_code))
        return None 
    
    if "blob" not in response_body["results"]:
        logger.critical("Error: did not receive blob from Registrar Server: %s"%str(response.status_code))
        return None 
    
    return response_body["results"]["blob"]
Example #9
0
 def do_cvreactivate(self):
     response = tornado_requests.request("PUT","http://%s:%s/v2/instances/%s/reactivate"%(self.cloudverifier_ip,self.cloudverifier_port,self.node_uuid),context=self.context,data=b'')
     if response.status_code != 200:
         logger.error("Update command response: %d Unexpected response from Cloud Verifier."%response.status_code)
         common.log_http_response(logger,logging.ERROR,response.json())
     else:
         logger.info("Node %s re-activated"%(self.node_uuid))
def doRegistrarDelete(registrar_ip,registrar_port, instance_id):
    global context
    response = tornado_requests.request("DELETE", "PUT",
                                        "http://%s:%s/v2/instances/%s"%(registrar_ip,registrar_port,instance_id),
                                        context=context)
    
    if response.status_code == 200:
        logger.debug("Registrar deleted.")
    else:
        logger.warn("Status command response: " + str(response.status_code) + " Unexpected response from Cloud Node.") 
        common.log_http_response(logger,logging.WARNING,response.json())
Example #11
0
    def do_verify(self):
        """initiaite v, instance_id and ip
        initiate the cloudinit sequence"""
        challenge = tpm_initialize.random_password(20)

        numtries = 0
        while True:
            try:
                response = tornado_requests.request(
                    "GET", "http://%s:%s/v2/keys/verify/challenge/%s/" %
                    (self.cloudnode_ip, self.cloudnode_port, challenge))
            except Exception as e:
                # this is one exception that should return a 'keep going' response
                if tornado_requests.is_refused(e):
                    numtries += 1
                    maxr = config.getint('tenant', 'max_retries')
                    if numtries >= maxr:
                        logger.error(
                            "Quitting after max number of retries to connect to %s"
                            % (self.cloudnode_ip))
                        raise e
                    retry = config.getfloat('tenant', 'retry_interval')
                    logger.info(
                        "Connection to %s refused %d/%d times, trying again in %f seconds..."
                        % (self.cloudnode_ip, numtries, maxr, retry))
                    time.sleep(retry)
                    continue
                else:
                    raise e

            response_body = response.json()
            if response.status_code == 200:
                if "results" not in response_body or 'hmac' not in response_body[
                        'results']:
                    logger.critical(
                        "Error: unexpected http response body from Cloud Node: %s"
                        % str(response.status_code))
                    break
                mac = response_body['results']['hmac']
                ex_mac = crypto.do_hmac(self.K, challenge)
                if mac == ex_mac:
                    logger.info("Key derivation successful")
                else:
                    logger.error("Key derivation failed")
            else:
                common.log_http_response(logger, logging.ERROR, response_body)
                retry = config.getfloat('tenant', 'retry_interval')
                logger.error(
                    "Key derivation not yet complete...trying again in %s seconds...Ctrl-C to stop"
                    % retry)
                time.sleep(retry)
                continue
            break
Example #12
0
    def do_cvstatus(self,listing=False):
        """initiaite v, agent_id and ip
        initiate the cloudinit sequence"""
        agent_uuid = ""
        if not listing:
            agent_uuid=self.agent_uuid

        response = tornado_requests.request("GET", "http://%s:%s/agents/%s"%(self.cloudverifier_ip,self.cloudverifier_port,agent_uuid),context=self.context)
        if response.status_code != 200:
            raise UserError("Status command response: %d Unexpected response from Cloud Verifier."%response.status_code)
            common.log_http_response(logger,logging.ERROR,response.json())
        else:
            logger.info("Agent Status %d: %s"%(response.status_code,response.json()))
def doActivateVirtualNode(registrar_ip,registrar_port,instance_id,deepquote):
    data = {
    'deepquote': deepquote,
    }
            
    v_json_message = json.dumps(data)
    
    response = tornado_requests.request("PUT",
                                        "http://%s:%s/v2/instances/%s/vactivate"%(registrar_ip,registrar_port,instance_id),
                                        data=v_json_message,
                                        context=None)

    if response.status_code == 200:
        logger.info("Registration activated for node %s."%instance_id)
        return True
    else:
        logger.error("Error: unexpected http response code from Registrar Server: " + str(response.status_code))
        common.log_http_response(logger,logging.ERROR,response.json())
        return False
def doActivateNode(registrar_ip,registrar_port,instance_id,key):
    data = {
    'auth_tag': crypto.do_hmac(base64.b64decode(key),instance_id),
    }
            
    v_json_message = json.dumps(data)
    
    response = tornado_requests.request("PUT",
                                        "http://%s:%s/v2/instances/%s/activate"%(registrar_ip,registrar_port,instance_id),
                                        data=v_json_message,
                                        context=None)

    if response.status_code == 200:
        logger.info("Registration activated for node %s."%instance_id)
        return True
    else:
        logger.error("Error: unexpected http response code from Registrar Server: " + str(response.status_code))
        common.log_http_response(logger,logging.ERROR,response.json())
        return False
Example #15
0
    def get(self):
        """This method handles the GET requests to retrieve status on agents from the WebApp. 
        
        Currently, only the web app is available for GETing, i.e. /agents. All other GET uri's 
        will return errors. 
        """

        rest_params = common.get_restful_params(self.request.uri)
        if rest_params is None:
            common.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ or /logs/ interface")
            return

        if "logs" in rest_params and rest_params["logs"] == "tenant":
            offset = 0
            if "pos" in rest_params and rest_params[
                    "pos"] is not None and rest_params["pos"].isdigit():
                offset = int(rest_params["pos"])
            # intercept requests for logs
            with open(keylime_logging.LOGSTREAM, 'r') as f:
                logValue = f.readlines()
                common.echo_json_response(self, 200, "Success",
                                          {'log': logValue[offset:]})
            return
        elif "agents" not in rest_params:
            # otherwise they must be looking for agent info
            common.echo_json_response(self, 400, "uri not supported")
            logger.warning('GET returning 400 response. uri not supported: ' +
                           self.request.path)
            return

        agent_id = rest_params["agents"]
        if agent_id is not None:
            # Handle request for specific agent data separately
            agents = self.get_agent_state(agent_id)
            agents["id"] = agent_id

            common.echo_json_response(self, 200, "Success", agents)
            return

        # If no agent ID, get list of all agents from Registrar
        try:
            response = tornado_requests.request(
                "GET",
                "http://%s:%s/agents/" %
                (tenant_templ.registrar_ip, tenant_templ.registrar_port),
                context=tenant_templ.context)
        except Exception as e:
            logger.error(
                "Status command response: %s:%s Unexpected response from Registrar."
                % (tenant_templ.registrar_ip, tenant_templ.registrar_port))
            logger.exception(e)
            common.echo_json_response(self, 500,
                                      "Unexpected response from Registrar",
                                      str(e))
            return

        response_body = response.json()

        if response.status_code != 200:
            logger.error(
                "Status command response: %d Unexpected response from Registrar."
                % response.status_code)
            common.log_http_response(logger, logging.ERROR, response_body)
            return None

        if ("results"
                not in response_body) or ("uuids"
                                          not in response_body["results"]):
            logger.critical(
                "Error: unexpected http response body from Registrar: %s" %
                str(response.status_code))
            return None

        agent_list = response_body["results"]["uuids"]

        # Loop through each agent and ask for status
        agents = {}
        for agent in agent_list:
            agents[agent] = self.get_agent_state(agent_id)

        # Pre-create sorted agents list
        sorted_by_state = {}
        states = cloud_verifier_common.CloudAgent_Operational_State.STR_MAPPINGS
        for state in states:
            sorted_by_state[state] = {}

        # Build sorted agents list
        for agent_id in agents:
            state = agents[agent_id]["operational_state"]
            sorted_by_state[state][agent_id] = agents[agent_id]

        print_order = [10, 9, 7, 3, 4, 5, 6, 2, 1, 8, 0]
        sorted_agents = []
        for state in print_order:
            for agent_id in sorted_by_state[state]:
                sorted_agents.append(agent_id)

        common.echo_json_response(self, 200, "Success",
                                  {'uuids': sorted_agents})
Example #16
0
    def do_quote(self):
        """initiaite v, agent_id and ip
        initiate the cloudinit sequence"""
        self.nonce = TPM_Utilities.random_password(20)

        numtries = 0
        response = None
        while True:
            # Get quote
            try:
                response = tornado_requests.request(
                    "GET", "http://%s:%s/quotes/identity?nonce=%s" %
                    (self.cloudagent_ip, self.cloudagent_port, self.nonce))
            except Exception as e:
                # this is one exception that should return a 'keep going' response
                if tornado_requests.is_refused(e):
                    numtries += 1
                    maxr = config.getint('tenant', 'max_retries')
                    if numtries >= maxr:
                        raise UserError(
                            "Quitting after max number of retries to connect to %s"
                            % (self.cloudagent_ip))
                    retry = config.getfloat('tenant', 'retry_interval')
                    logger.info(
                        "Connection to %s refused %d/%d times, trying again in %f seconds..."
                        % (self.cloudagent_ip, numtries, maxr, retry))
                    time.sleep(retry)
                    continue
                else:
                    raise e
            break

        try:
            if response is not None and response.status_code != 200:
                raise UserError(
                    "Status command response: %d Unexpected response from Cloud Agent."
                    % response.status_code)

            response_body = response.json()

            if "results" not in response_body:
                raise UserError(
                    "Error: unexpected http response body from Cloud Agent: %s"
                    % str(response.status_code))

            quote = response_body["results"]["quote"]
            logger.debug("agent_quote received quote:" + quote)

            public_key = response_body["results"]["pubkey"]
            logger.debug("agent_quote received public key:" + public_key)

            # Get tpm_version, hash_alg
            tpm_version = response_body["results"]["tpm_version"]
            logger.debug("agent_quote received tpm version:" +
                         str(tpm_version))

            # Ensure hash_alg is in accept_tpm_hash_algs list
            hash_alg = response_body["results"]["hash_alg"]
            logger.debug("agent_quote received hash algorithm:" + hash_alg)
            if not Hash_Algorithms.is_accepted(
                    hash_alg,
                    config.get('tenant', 'accept_tpm_hash_algs').split(',')):
                raise UserError(
                    "TPM Quote is using an unaccepted hash algorithm: %s" %
                    hash_alg)

            # Ensure enc_alg is in accept_tpm_encryption_algs list
            enc_alg = response_body["results"]["enc_alg"]
            logger.debug("agent_quote received encryption algorithm:" +
                         enc_alg)
            if not Encrypt_Algorithms.is_accepted(
                    enc_alg,
                    config.get('tenant',
                               'accept_tpm_encryption_algs').split(',')):
                raise UserError(
                    "TPM Quote is using an unaccepted encryption algorithm: %s"
                    % enc_alg)

            # Ensure sign_alg is in accept_tpm_encryption_algs list
            sign_alg = response_body["results"]["sign_alg"]
            logger.debug("agent_quote received signing algorithm:" + sign_alg)
            if not Sign_Algorithms.is_accepted(
                    sign_alg,
                    config.get('tenant',
                               'accept_tpm_signing_algs').split(',')):
                raise UserError(
                    "TPM Quote is using an unaccepted signing algorithm: %s" %
                    sign_alg)

            if not self.validate_tpm_quote(public_key, quote, tpm_version,
                                           hash_alg):
                raise UserError(
                    "TPM Quote from cloud agent is invalid for nonce: %s" %
                    self.nonce)

            logger.info("Quote from %s validated" % self.cloudagent_ip)

            # encrypt U with the public key
            encrypted_U = crypto.rsa_encrypt(
                crypto.rsa_import_pubkey(public_key), str(self.U))

            b64_encrypted_u = base64.b64encode(encrypted_U)
            logger.debug("b64_encrypted_u: " + b64_encrypted_u)
            data = {
                'encrypted_key': b64_encrypted_u,
                'auth_tag': self.auth_tag
            }

            if self.payload is not None:
                data['payload'] = self.payload

            u_json_message = json.dumps(data)

            #post encrypted U back to CloudAgent
            response = tornado_requests.request(
                "POST",
                "http://%s:%s/keys/ukey" %
                (self.cloudagent_ip, self.cloudagent_port),
                data=u_json_message)

            if response.status_code != 200:
                common.log_http_response(logger, logging.ERROR, response_body)
                raise UserError(
                    "Posting of Encrypted U to the Cloud Agent failed with response code %d"
                    % response.status_code)

        except Exception as e:
            self.do_cvstop()
            raise e
Example #17
0
    def do_quote(self):
        """initiaite v, instance_id and ip
        initiate the cloudinit sequence"""
        self.nonce = tpm_initialize.random_password(20)

        numtries = 0
        response = None
        while True:
            # Get quote
            try:
                response = tornado_requests.request(
                    "GET", "http://%s:%s/v2/quotes/identity/nonce/%s/" %
                    (self.cloudnode_ip, self.cloudnode_port, self.nonce))
            except Exception as e:
                # this is one exception that should return a 'keep going' response
                if tornado_requests.is_refused(e):
                    numtries += 1
                    maxr = config.getint('tenant', 'max_retries')
                    if numtries >= maxr:
                        logger.error(
                            "Quitting after max number of retries to connect to %s"
                            % (self.cloudnode_ip))
                        raise e
                    retry = config.getfloat('tenant', 'retry_interval')
                    logger.info(
                        "Connection to %s refused %d/%d times, trying again in %f seconds..."
                        % (self.cloudnode_ip, numtries, maxr, retry))
                    time.sleep(retry)
                    continue
                else:
                    raise e
            break

        try:
            if response is not None and response.status_code != 200:
                raise Exception(
                    "Status command response: %d Unexpected response from Cloud Node."
                    % response.status_code)

            response_body = response.json()

            if "results" not in response_body:
                raise Exception(
                    "Error: unexpected http response body from Cloud Node: %s"
                    % str(response.status_code))

            quote = response_body["results"]["quote"]
            logger.debug("cnquote received quote:" + quote)

            public_key = response_body["results"]["pubkey"]
            logger.debug("cnquote received public key:" + public_key)

            if not self.validate_tpm_quote(public_key, quote):
                raise Exception(
                    "TPM Quote from cloud node is invalid for nonce: %s" %
                    self.nonce)

            logger.info("Quote from %s validated" % self.cloudnode_ip)

            # encrypt U with the public key
            encrypted_U = crypto.rsa_encrypt(
                crypto.rsa_import_pubkey(public_key), str(self.U))

            b64_encrypted_u = base64.b64encode(encrypted_U)
            logger.debug("b64_encrypted_u: " + b64_encrypted_u)
            data = {
                'encrypted_key': b64_encrypted_u,
                'auth_tag': self.auth_tag
            }

            if self.payload is not None:
                data['payload'] = self.payload

            u_json_message = json.dumps(data)

            #post encrypted U back to CloudNode
            response = tornado_requests.request(
                "POST",
                "http://%s:%s/v2/keys/ukey" %
                (self.cloudnode_ip, self.cloudnode_port),
                data=u_json_message)

            if response.status_code != 200:
                common.log_http_response(logger, logging.ERROR, response_body)
                raise Exception(
                    "Posting of Encrypted U to the Cloud Node failed with response code %d"
                    % response.status_code)

        except Exception as e:
            logger.error(e)
            self.do_cvstop()
 def get(self):
     """This method handles the GET requests to retrieve status on instances for all nodes in a Web-based GUI.
     
     Currently, only the web app is available for GETing, i.e. /webapp. All other GET uri's 
     will return errors. 
     """
     
     # Get list of instances from Registrar  
     try:
         response = tornado_requests.request("GET",
                                     "http://%s:%s/v2/nodes/"%(tenant_templ.webapp_ip,tenant_templ.webapp_port),context=tenant_templ.context)
     except Exception as e:
         logger.error("Status command response: %s:%s Unexpected response from WebApp API."%(tenant_templ.webapp_ip,tenant_templ.webapp_port))
         logger.error(traceback.print_exc())
         logger.error("Error: %s "%str(e))
         raise e
     
     response_body = response.json()
     
     if response.status_code != 200:
         logger.error("Status command response: %d Unexpected response from WebApp API."%response.status_code)
         common.log_http_response(logger,logging.ERROR,response_body)
         return None
     
     if ("results" not in response_body) or ("uuids" not in response_body["results"]):
         logger.critical("Error: unexpected http response body from WebApp API: %s"%str(response.status_code))
         return None 
     
     instance_list = response_body["results"]["uuids"]
     
     # Get default policies for TPM/vTPM from config as suggestions to user 
     tpm_policy = json.dumps(json.loads(config.get('tenant', 'tpm_policy')), indent=2)
     vtpm_policy = json.dumps(json.loads(config.get('tenant', 'vtpm_policy')), indent=2)
     
     self.set_status(200)
     self.set_header('Content-Type', 'text/html')
     self.write(
         """
         <!DOCTYPE html>
         <html>
             <head>
                 <meta charset='UTF-8'>
                 <title>Advanced Tenant Management System</title>
                 <script type='text/javascript' src='/static/js/webapp.js'></script>
                 <link href='/static/css/webapp.css' rel='stylesheet' type='text/css'/>
             </head>
             <body>
                 <div id='modal_box' onclick="if (event.target == this) {toggleVisibility(this.id);resetAddNodeForm();return false;}">
                 
         """
     )
     
     self.write(
         """
                     <div id='modal_body'>
                         <center>
                             <h3>Add Node</h3>
                             <h4 id='uuid_str'></h4>
                         </center>
                         <form id='add_node' name='add_node' onsubmit='submitAddNodeForm(this); return false;'>
                             <div class="form_block">
                                 <label for='node_ip'>Node IP: </label>
                                 <input type='text' id='node_ip' name='node_ip' value='127.0.0.1' required onfocus='this.select()'>
                                 <br>
                             </div>
                             
                             <div id='imalist_toggle' onclick="toggleVisibility('imalist_block');" title='IMA Configuration'>
                                 IMA Configuration
                             </div>
                             <div id="imalist_block">
                                 <div class="form_block">
                                     <label for='w_list'>Whitelist: </label>
                                     <div id='w_list' name='w_list' class='file_drop'>
                                         <i>Drag payload here &hellip;</i>
                                     </div>
                                     <input type='hidden' name='w_list_data' id='w_list_data' value=''>
                                     <input type='hidden' name='w_list_name' id='w_list_name' value=''>
                                     <br>
                                 </div>
                                 
                                 <div class="form_block">
                                     <label for='e_list'>Exclude: </label>
                                     <div id='e_list' name='e_list' class='file_drop'>
                                         <i>Drag payload here &hellip;</i>
                                     </div>
                                     <input type='hidden' name='e_list_data' id='e_list_data' value=''>
                                     <input type='hidden' name='e_list_name' id='e_list_name' value=''>
                                     <br>
                                 </div>
                             </div>
                             <br>
                             
                             <div id='policy_toggle' onclick="toggleVisibility('policy_block');" title='TPM &amp; vTPM Policy Configuration'>
                                 TPM &amp; vTPM Policy Configuration
                             </div>
                             <div id="policy_block">
                                 <div class="form_block">
                                     <label for='tpm_policy'>TPM Policy: </label><br>
                                     <textarea class='json_input' id='tpm_policy' name='tpm_policy'>{0}</textarea>
                                     <br>
                                 </div>
                                 
                                 <div class="form_block">
                                     <label for='vtpm_policy'>vTPM Policy: </label><br>
                                     <textarea class='json_input' id='vtpm_policy' name='vtpm_policy'>{1}</textarea>
                                     <br>
                                 </div>
                             </div>
                             <br>
         """.format(tpm_policy, vtpm_policy)
     )
     
     self.write(
         """
                             <div id="payload_block">
                                 <div class="form_block">
                                     <label for='ptype'>Payload type: </label>
                                     <label><input type='radio' name='ptype' value='{0}' checked="checked" onclick='toggleTabs(this.value)'> File </label>&nbsp;
                                     <label><input type='radio' name='ptype' value='{1}' onclick='toggleTabs(this.value)'> Keyfile </label>&nbsp;
                                     <label><input type='radio' name='ptype' value='{2}' onclick='toggleTabs(this.value)'> CA Dir </label>&nbsp;
                                     <br>
                                 </div>
         """.format(Node_Init_Types.FILE, Node_Init_Types.KEYFILE, Node_Init_Types.CA_DIR)
     )
     
     self.write(
         """
                                 <div id='keyfile_container' class="form_block" style="display:none;">
                                     <label for='file'>Keyfile: </label>
                                     <div id='keyfile' name='keyfile' class='file_drop'>
                                         <i>Drag key file here &hellip;</i>
                                     </div>
                                     <input type='hidden' name='keyfile_data' id='keyfile_data' value=''>
                                     <input type='hidden' name='keyfile_name' id='keyfile_name' value=''>
                                     <br>
                                 </div>
                                 
                                 <div id='file_container' class="form_block">
                                     <label for='file'>Payload: </label>
                                     <div id='file' name='file' class='file_drop'>
                                         <i>Drag payload here &hellip;</i>
                                     </div>
                                     <input type='hidden' name='file_data' id='file_data' value=''>
                                     <input type='hidden' name='file_name' id='file_name' value=''>
                                     <br>
                                 </div>
                                 
                                 <div id='ca_dir_container' style="display:none;">
                                     <div class="form_block">
                                         <label for='ca_dir'>CA Dir: </label>
                                         <input type='text' id='ca_dir' name='ca_dir' placeholder='e.g., default'>
                                         <br>
                                     </div>
                                     
                                     <div class="form_block">
                                         <label for='ca_dir_pw'>CA Password: </label>
                                         <input type='password' id='ca_dir_pw' name='ca_dir_pw' placeholder='e.g., default'>
                                         <br>
                                     </div>
                                     
                                     <div class="form_block">
                                         <label for='include_dir'>Include dir: </label>
                                         <div id='include_dir' name='include_dir' class='file_drop multi_file'>
                                             <i>Drag files here &hellip;</i>
                                         </div>
                                         <input type='hidden' name='include_dir_data' id='include_dir_data' value=''>
                                         <input type='hidden' name='include_dir_name' id='include_dir_name' value=''>
                                         <br>
                                     </div>
                                 </div>
                             </div>
                             <br>
                             
                             <input type='hidden' name='uuid' id='uuid' value=''>
                             <center><button type="submit" value="Add Node">Add Node</button></center>
                             <br>
                         </form>
                     </div>
                 </div>
                 
                 <div id="header">
                     <div class="logo" title="Keylime">&nbsp;</div>
                     <div id="header_banner">
                         <h1>Keylime Advanced Tenant Management System</h1>
                     </div>
                     <div class="logo" style="float:right;" title="Keylime">&nbsp;</div>
                    <br style="clear:both;">
                 </div>
                 
                 <div id="instance_body">
                     <h2>Instances</h2>
                     <div class='table_header'>
                         <div class='table_control'>&nbsp;</div>
                         <div class='table_col'>UUID</div>
                         <div class='table_col'>address</div>
                         <div class='table_col'>status</div>
                         <br style='clear:both;' />
                     </div>
                     <div id='node_container'>
         """
     )
     
     # Print out all instance node entries (if any) 
     if len(instance_list) == 0:
         self.write(
             """
                         <div style="color:#888;margin-left:15px;padding:10px;">
                             <i>No nodes registered</i>
                         </div>
             """
         )
     else:
         for instance_id in instance_list:
             self.write(
                 """
                         <div id='{0}'>
                             <div id='{0}-over' style='display:block;cursor:help;width:800px;'></div>
                             <div id='{0}-det' style='display:none;'></div>
                         </div>
                 """.format(instance_id)
             )
     
     self.write(
         """
                     </div>
                 </div>
             </body>
         </html>
         """
     )
 def get(self):
     """This method handles the GET requests to retrieve status on instances from the WebApp. 
     
     Currently, only the web app is available for GETing, i.e. /v2/nodes. All other GET uri's 
     will return errors. 
     """
     
     rest_params = common.get_restful_params(self.request.path)
     if rest_params is None:
         common.echo_json_response(self, 405, "Not Implemented: Use /v2/nodes/ interface")
         return
     
     if "nodes" not in rest_params:
         common.echo_json_response(self, 400, "uri not supported")
         logger.warning('GET returning 400 response. uri not supported: ' + self.request.path)
         return
     
     instance_id = rest_params["nodes"]
     
     if instance_id is not None:
         instances = self.get_instance_state(instance_id)
         instances["id"] = instance_id
         
         common.echo_json_response(self, 200, "Success", instances)
     else:
         # Get list of instances from Registrar  
         try:
             response = tornado_requests.request("GET",
                                         "http://%s:%s/v2/instances/"%(tenant_templ.registrar_ip,tenant_templ.registrar_port),context=tenant_templ.context)
         except Exception as e:
             logger.error("Status command response: %s:%s Unexpected response from Registrar."%(tenant_templ.registrar_ip,tenant_templ.registrar_port))
             logger.error(traceback.print_exc())
             logger.error("Error: %s "%str(e))
             raise e
         
         response_body = response.json()
         
         if response.status_code != 200:
             logger.error("Status command response: %d Unexpected response from Registrar."%response.status_code)
             common.log_http_response(logger,logging.ERROR,response_body)
             return None
         
         if ("results" not in response_body) or ("uuids" not in response_body["results"]):
             logger.critical("Error: unexpected http response body from Registrar: %s"%str(response.status_code))
             return None 
         
         instance_list = response_body["results"]["uuids"]
         
         # Loop through each instance and ask for status
         instances = {}
         for instance in instance_list:
             instances[instance] = self.get_instance_state(instance_id)
         
         # Pre-create sorted instances list 
         sorted_by_state = {}
         states = cloud_verifier_common.CloudInstance_Operational_State.STR_MAPPINGS
         for state in states:
             sorted_by_state[state] = {}
         
         # Build sorted instances list 
         for instance_id in instances:
             state = instances[instance_id]["operational_state"]
             sorted_by_state[state][instance_id] = instances[instance_id]
         
         print_order = [9,7,3,4,5,6,2,1,8,0]
         sorted_instances = []
         for state in print_order:
             for instance_id in sorted_by_state[state]:
                 sorted_instances.append(instance_id)
         
         common.echo_json_response(self, 200, "Success", {'uuids':sorted_instances})