def route_request(self, handle, connection, command, headers, data): if self.node.quitting: # Answer internal error on all requests while quitting, assume client can handle that # TODO: Answer permantely moved (301) instead with header Location: <another-calvin-runtime>??? self.send_response(handle, connection, None, status=calvinresponse.INTERNAL_ERROR) return try: issuetracker = IssueTracker() handler, match = self._handler_for_route(command) if handler: credentials = None if data: data = json.loads(data) _log.debug("Calvin control handles:%s\n%s\n---------------" % (command, data)) handler(handle, connection, match, data, headers) else: _log.error("No route found for: %s\n%s" % (command, data)) self.send_response(handle, connection, None, status=404) except Exception as e: _log.info("Failed to parse request", exc_info=e) self.send_response(handle, connection, None, status=calvinresponse.BAD_REQUEST)
def process(self, source_file, issuetracker=None): path = os.path.dirname(source_file) self.path = _expand_path(path) self.source_file = _expand_path(source_file) self.issuetracker = issuetracker or IssueTracker() self.line_number = 0 return self._process(), self.issuetracker
def get_components(filename, names): try: with open(filename, 'r') as source: source_text = source.read() except: from calvin.utilities.issuetracker import IssueTracker it = IssueTracker() it.add_error('File not found', {'script': filename}) return [], it return calvin_components(source_text, names)
def parse(self, source_text): # return ir (AST) and issuetracker self.issuetracker = IssueTracker() self.source_text = source_text try: ir = self.parser.parse(source_text) except SyntaxError as e: self.issuetracker.add_error(e.text, { 'line': e.lineno, 'col': e.offset }) ir = ast.Node() return ir, self.issuetracker
def parse(self, source_text, logger=None): # return ir (AST) and issuetracker self.issuetracker = IssueTracker() self.source_text = source_text root = None try: root = self.parser.parse(source_text, debug=logger) except SyntaxError as e: self.issuetracker.add_error(e.text, {'line':e.lineno, 'col':e.offset}) finally: ir = root or ast.Node() return ir, self.issuetracker
def _handle_policy_decision(data, appname, verify, access_decision, org_cb, security=None): if not access_decision: _log.error("Access denied") # This error reason is detected in calvin control and gives proper REST response _exit_with_error(org_cb) return if 'app_info' not in data and 'script' in data: deployable, issuetracker = compile_script(data['script'], appname) elif 'app_info' in data: deployable = data['app_info'] issuetracker = IssueTracker() else: _log.error("Neither app_info or script supplied") # This error reason is detected in calvin control and gives proper REST response _exit_with_error(org_cb) return org_cb(deployable, issuetracker, security=security)
def visualize_component(source_text, name): # STUB from calvin.utilities.issuetracker import IssueTracker it = IssueTracker() it.add_error('Visualizing components not yet implemented.') return "digraph structs {ERROR}", it
def _exit_with_error(callback): """Helper method to generate a proper error""" it = IssueTracker() it.add_error("UNAUTHORIZED", info={'status': 401}) callback({}, it) return
def compile_script_check_security(data, filename, cb, security=None, content=None, verify=True, node=None, signature=None): """ Compile a script and return a tuple (deployable, errors, warnings). 'credentials' are optional security credentials(?) 'verify' is deprecated and will be removed 'node' is the runtime performing security check(?) 'cb' is a CalvinCB callback N.B. If callback 'cb' is given, this method calls cb(deployable, errors, warnings) and returns None N.B. If callback 'cb' is given, and method runs to completion, cb is called with additional parameter 'security' (?) """ def _exit_with_error(callback): """Helper method to generate a proper error""" it = IssueTracker() it.add_error("UNAUTHORIZED", info={'status': 401}) callback({}, it) return def _handle_policy_decision(data, appname, verify, access_decision, org_cb, security=None): if not access_decision: _log.error("Access denied") # This error reason is detected in calvin control and gives proper REST response _exit_with_error(org_cb) return if 'app_info' not in data and 'script' in data: deployable, issuetracker = compile_script(data['script'], appname) elif 'app_info' in data: deployable = data['app_info'] issuetracker = IssueTracker() else: _log.error("Neither app_info or script supplied") # This error reason is detected in calvin control and gives proper REST response _exit_with_error(org_cb) return org_cb(deployable, issuetracker, security=security) # # Actual code for compile_script # appname = appname_from_filename(filename) # FIXME: if node is None we bypass security even if enabled. Is that the intention? if node is not None and security_enabled(): # FIXME: If cb is None, we will return from this method with None instead of a tuple, failing silently if security: sec = security else: sec = Security(node) verified, signer = sec.verify_signature_content(content, "application") if not verified: # Verification not OK if sign or cert not OK. _log.error("Failed application verification") # This error reason is detected in calvin control and gives proper REST response _exit_with_error(cb) return sec.check_security_policy(CalvinCB(_handle_policy_decision, data, appname, verify, security=security, org_cb=cb), element_type="application", element_value=signer) return # # We get here if node is None, or security is disabled # # This used to be # _handle_policy_decision(data, filename, verify, access_decision=True, security=None, org_cb=cb) # but since _handle_policy_decision is called with access_decision=True, security=None only compile_script would be called if 'app_info' not in data and 'script' in data: deployable, issuetracker = compile_script(data['script'], appname) elif 'app_info' in data: deployable = data['app_info'] issuetracker = IssueTracker() else: _log.error("Neither app_info or script supplied") # This error reason is detected in calvin control and gives proper REST response _exit_with_error(org_cb) return cb(deployable, issuetracker, security=None)
def handle_deploy(self, handle, connection, match, data, hdr): """ POST /deploy Compile and deploy a calvin script to this calvin node Apply deployment requirements to actors of an application and initiate migration of actors accordingly Body: { "name": <application name>, "script": <calvin script> # alternativly "app_info" "app_info": <compiled script as app_info> # alternativly "script" "sec_sign": {<cert hash>: <security signature of script>, ...} # optional and only with "script" "sec_credentials": <security credentials of user> # optional "deploy_info": {"groups": {"<group 1 name>": ["<actor instance 1 name>", ...]}, # TODO not yet implemented "requirements": { "<actor instance 1 name>": [ {"op": "<matching rule name>", "kwargs": {<rule param key>: <rule param value>, ...}, "type": "+" or "-" for set intersection or set removal, respectively }, ... ], ... } } } Note that either a script or app_info must be supplied. Optionally security verification of application script can be made. Also optionally user credentials can be supplied, some runtimes are configured to require credentials. The credentials takes for example the following form: {"user": <username>, "password": <password>, "role": <role>, "group": <group>, ... } The matching rules are implemented as plug-ins, intended to be extended. The type "+" is "and"-ing rules together (actually the intersection of all possible nodes returned by the rules.) The type "-" is explicitly removing the nodes returned by this rule from the set of possible nodes. Note that only negative rules will result in no possible nodes, i.e. there is no implied "all but these." A special matching rule exist, to first form a union between matching rules, i.e. alternative matches. This is useful for e.g. alternative namings, ownerships or specifying either of two specific nodes. {"op": "union_group", "requirements": [list as above of matching rules but without type key] "type": "+" } Other matching rules available is current_node, all_nodes and node_attr_match which takes an index param which is attribute formatted, e.g. {"op": "node_attr_match", "kwargs": {"index": ["node_name", {"organization": "org.testexample", "name": "testNode1"}]} "type": "+" } Response status code: OK, CREATED, BAD_REQUEST, UNAUTHORIZED or INTERNAL_ERROR Response: {"application_id": <application-id>, "actor_map": {<actor name with namespace>: <actor id>, ...} "placement": {<actor_id>: <node_id>, ...}, "requirements_fulfilled": True/False} Failure response: {'errors': <compilation errors>, 'warnings': <compilation warnings>, 'exception': <exception string>} """ try: _log.analyze(self.node.id, "+", data) if 'app_info' not in data: kwargs = {} credentials = "" # Supply security verification data when available content = None if "sec_credentials" in data: credentials = data['sec_credentials'] content = {} if not "sec_sign" in data: data['sec_sign'] = {} content = { 'file': data["script"], 'sign': { h: s.decode('hex_codec') for h, s in data['sec_sign'].iteritems() } } compiler.compile_script_check_security( data["script"], filename=data["name"], security=self.security, content=content, node=self.node, verify=(data["check"] if "check" in data else True), cb=CalvinCB(self.handle_deploy_cont, handle=handle, connection=connection, data=data), **kwargs) else: # Supplying app_info is for backward compatibility hence abort if node configured security # Main user is csruntime when deploying script at the same time and some tests used # via calvin.Tools.deployer (the Deployer below is the new in appmanager) # TODO rewrite these users to send the uncompiled script as cscontrol does. if security_enabled(): _log.error( "Can't combine compiled script with runtime having security" ) self.send_response(handle, connection, None, status=calvinresponse.UNAUTHORIZED) return app_info = data['app_info'] issuetracker = IssueTracker() self.handle_deploy_cont(app_info, issuetracker, handle, connection, data) except Exception as e: _log.exception("Deployer failed, e={}".format(e)) self.send_response(handle, connection, json.dumps({'exception': str(e)}), status=calvinresponse.INTERNAL_ERROR)