def execute(self, params: Kwargs, timeout=None, context: Context = None) -> CompletedProcess: """ StepExecution wrapper Parameters ---------- properties: properties passed to execute implementation env: environment variables Returns ------- StepExecution: dataclass containing all the information from the result execution """ start = get_now() try: return self._execute(params, timeout=timeout, context=context) except Exception as e: return CompletedProcess( success=False, stderr=f"Error creating execution: {format_exception(e)}", start_time=start, end_time=get_now())
def _main_loop(self): with self.dm.flask_app.app_context(): last_shutdown = Parameter.get('last_graceful_shutdown') try: last_shutdown = dt.datetime.strptime(last_shutdown, defaults.DATETIME_FORMAT) except: last_shutdown = get_now() else: last_shutdown = last_shutdown if self.dm.config.force_scan or last_shutdown < ( get_now() - dt.timedelta(seconds=self.refresh_interval)): scan = True else: scan = False changed_routes = self._loop.run_until_complete( self._async_refresh_route_table(discover_new_neighbours=scan, check_current_neighbours=scan, max_num_discovery=None)) self.session.commit() self.publish_q.safe_put(InitialRouteSet()) super()._main_loop() Parameter.set('last_graceful_shutdown', get_now().strftime(defaults.DATETIME_FORMAT))
def _deploy_orchestration(orchestration: Orchestration, var_context: 'Context', hosts: t.Dict[str, t.List[Id]], execution: OrchExecution, lock_retries, lock_delay, timeout ) -> Id: """ Parameters ---------- orchestration orchestration to deploy params parameters to pass to the steps Returns ------- t.Tuple[bool, bool, t.Dict[int, dpl.CompletedProcess]]: tuple with 3 values. (boolean indicating if invoke process ended up successfully, boolean indicating if undo process ended up successfully, dict with all the executions). If undo process not executed, boolean set to None """ rse = RegisterStepExecution(execution) kwargs = dict() kwargs['start_time'] = execution.start_time or get_now() cc = create_cmd_from_orchestration(orchestration, var_context, hosts=hosts, register=rse, executor=executor) # convert UUID into str as in_ filter does not handle UUID type all = [str(s) for s in hosts['all']] servers = Server.query.filter(Server.id.in_(all)).all() scope_enabled = locker_scope_enabled(Scope.ORCHESTRATION) if scope_enabled: try: applicant = lock.lock(Scope.ORCHESTRATION, servers, applicant=var_context.env.get('root_orch_execution_id'), retries=lock_retries, delay=lock_delay) except errors.LockError as e: kwargs.update(success=False, message=str(e)) rse.update_orch_execution(**kwargs) raise try: kwargs['success'] = cc.invoke(timeout=timeout) if not kwargs['success'] and orchestration.undo_on_error: kwargs['undo_success'] = cc.undo() kwargs['end_time'] = get_now() rse.update_orch_execution(**kwargs) except Exception as e: current_app.logger.exception("Exception while executing invocation command") kwargs.update(success=False, message=str(e)) rse.update_orch_execution(**kwargs) try: db.session.rollback() except: pass finally: if scope_enabled: lock.unlock(Scope.ORCHESTRATION, applicant=applicant, servers=servers) return execution.id
def post(self, transfer_id): """Generates the chunk into disk""" data = request.get_json() trans: Transfer = Transfer.query.get_or_raise(transfer_id) if trans.status == TransferStatus.WAITING_CHUNKS: trans.started_on = get_now() trans.status = TransferStatus.IN_PROGRESS db.session.commit() elif trans.status != TransferStatus.IN_PROGRESS: raise errors.TransferNotInValidState(transfer_id, trans.status.name) chunk = data.get('content') chunk_id = data.get('chunk') if trans.num_chunks == 1: file = os.path.join(trans.dest_path, f'{trans.filename}') else: file = os.path.join(trans.dest_path, f'{trans.filename}_chunk.{chunk_id}') with open(file, 'wb') as fd: raw = base64.b64decode(chunk.encode('ascii')) fd.write(raw) if trans.num_chunks == 1: msg = f"File {trans.filename} from transfer {transfer_id} generated successfully" trans.status = TransferStatus.COMPLETED trans.ended_on = get_now() db.session.commit() else: msg = f"Chunk {chunk_id} from transfer {transfer_id} generated successfully" current_app.logger.debug(msg) return {'message': msg}, 201
def test_composite_command_error2(self): mocked_imp_succ = mock.Mock() mocked_imp_error = mock.Mock() start_time = get_now() end_time = get_now() + datetime.timedelta(5 / (24 * 60 * 60)) mocked_imp_succ.execute.return_value = StepExecution( success=True, stdout='stdout', stderr='stderr', rc=0, start_time=start_time, end_time=end_time) mocked_imp_error.execute.return_value = StepExecution( success=False, stdout='stdout', stderr='stderr', rc=0, start_time=start_time, end_time=end_time) uc1 = UndoCommand(implementation=mocked_imp_succ, id_=1) uc2 = UndoCommand(implementation=mocked_imp_succ, id_=2) uc3 = UndoCommand(implementation=mocked_imp_succ, id_=3) uc4 = UndoCommand(implementation=mocked_imp_succ, id_=4) uc5 = UndoCommand(implementation=mocked_imp_succ, id_=5) ccu1 = CompositeCommand({uc1: []}, async_operator=self.ao) ccu2 = CompositeCommand({ uc2: [uc3], uc3: [uc4] }, async_operator=self.ao) c1 = Command(implementation=mocked_imp_succ, undo_implementation=ccu1, id_=6) c2 = Command(implementation=mocked_imp_error, undo_implementation=ccu2, id_=7) c3 = Command(implementation=mocked_imp_succ, undo_implementation=uc5, id_=8) cc = CompositeCommand({c1: [c2], c2: [c3]}, async_operator=self.ao) res = cc.invoke() self.assertEqual(False, res) self.assertEqual(1, mocked_imp_succ.execute.call_count) self.assertEqual(1, mocked_imp_error.execute.call_count) res = cc.undo() self.assertEqual(True, res) self.assertEqual(2, mocked_imp_succ.execute.call_count) self.assertEqual(1, mocked_imp_error.execute.call_count)
def ping(): req_data = request.get_json() if req_data: req_data.update(dest_time=get_now().strftime(defaults.DATETIME_FORMAT)) if 'servers' not in req_data: req_data.update(servers={}) else: req_data = dict(dest_time=get_now().strftime(defaults.DATETIME_FORMAT)) return req_data, 200
def _execute(self, params: Kwargs, timeout=None, context: Context = None) -> CompletedProcess: tokens = self.rpl_params(**params, env=context.env) return CompletedProcess(success=True, stdout=tokens, rc=0, start_time=get_now(), end_time=get_now())
def test_undo_on_error(self): mocked_imp_succ = mock.Mock() mocked_imp_error = mock.Mock() mocked_imp_succ.execute.return_value = StepExecution( success=True, stdout='stdout', stderr='stderr', rc=0, start_time=get_now(), end_time=get_now() + datetime.timedelta(5 / (24 * 60 * 60))) mocked_imp_error.execute.return_value = StepExecution( success=False, stdout='stdout', stderr='stderr', rc=0, start_time=get_now(), end_time=get_now() + datetime.timedelta(5 / (24 * 60 * 60))) uc1 = UndoCommand(implementation=mocked_imp_succ, id_=1) uc2 = UndoCommand(implementation=mocked_imp_error, id_=2) uc3 = UndoCommand(implementation=mocked_imp_succ, id_=3) c1 = Command(implementation=mocked_imp_succ, undo_implementation=uc1, id_=1) c2 = Command(implementation=mocked_imp_succ, undo_implementation=uc2, id_=2) c3 = Command(implementation=mocked_imp_error, undo_implementation=uc3, undo_on_error=True, id_=3) cc1 = CompositeCommand({ c1: [c2], c2: [c3] }, force_all=True, id_=1, async_operator=self.ao) res = cc1.invoke() self.assertFalse(res) res = cc1.undo() self.assertEqual(3, mocked_imp_succ.execute.call_count) self.assertEqual(2, mocked_imp_error.execute.call_count) self.assertFalse(res)
def _notify_cluster_out(self): with self.dm.flask_app.app_context(): servers = Server.get_neighbours() if servers: self.logger.debug( f"Sending shutdown to {', '.join([s.name for s in servers])}" ) else: self.logger.debug("No server to send shutdown information") if servers: responses = asyncio.run( ntwrk.parallel_requests( servers, 'post', view_or_url='api_1_0.cluster_out', view_data=dict(server_id=str(Server.get_current().id)), json={ 'death': get_now().strftime(defaults.DATEMARK_FORMAT) }, timeout=2, auth=get_root_auth())) if self.logger.level <= logging.DEBUG: for r in responses: if not r.ok: self.logger.warning( f"Unable to send data to {r.server}: {r}")
def _proxy_request(request: 'flask.Request', destination: Server, verify=False) -> requests.Response: url = destination.url() + request.full_path req_data = request.get_json() if request.path == '/ping': server_data = {'id': str(g.server.id), 'name': g.server.name, 'time': get_now().strftime(defaults.DATETIME_FORMAT)} if req_data: if 'servers' not in req_data: req_data['servers'] = {} req_data['servers'].update({len(req_data['servers']) + 1: server_data}) else: req_data = dict(servers={1: server_data}) kwargs = { 'json': req_data, 'allow_redirects': False } headers = {key.lower(): value for key, value in request.headers.items()} # Let requests reset the host for us. if 'host' in headers: del headers['host'] headers['d-source'] = headers.get('d-source', '') + ':' + str(g.server.id) kwargs['headers'] = headers cookies = request.cookies kwargs['cookies'] = cookies return requests.request(request.method, url, verify=verify, **kwargs)
def test_set_and_get(self): p = Parameter( 'data', dump=lambda x: x.strftime(defaults.DATETIME_FORMAT), load=lambda x: dt.datetime.strptime(x, defaults.DATETIME_FORMAT)) db.session.add(p) now = get_now() Parameter.set('data', now) p = Parameter.query.get('data') self.assertEqual(now.strftime(defaults.DATETIME_FORMAT), p.value) p = Parameter('integer', load=int, dump=str) db.session.add(p) Parameter.set('integer', 5) p = Parameter.query.get('integer') self.assertEqual('5', p.value) p = Parameter('name') db.session.add(p) Parameter.set('name', 'Joan') p = Parameter.query.get('name') self.assertEqual('Joan', p.value) # test get self.assertEqual(now, Parameter.get('data')) self.assertEqual(5, Parameter.get('integer')) self.assertEqual('Joan', Parameter.get('name')) p = Parameter('none') db.session.add(p) self.assertIsNone(Parameter.get('none')) self.assertEqual('default', Parameter.get('none', 'default'))
def set_alive(self, iden: Id, session: Id, alive=None) -> t.Optional[t.Dict]: with self._lock: alive = alive or get_now() if iden in self._cluster: if self._cluster[iden].session == session: if self._cluster[iden].death is not None: if alive < self._cluster[iden].birth: self._cluster[iden].birth = alive self._cluster[iden].keepalive = alive self._cluster[iden].death = None else: return None else: self._cluster[iden].session = session self._cluster[iden].birth = alive self._cluster[iden].keepalive = alive self._cluster[iden].death = None else: self._cluster[iden] = self._register_class(iden, session=session, birth=alive, keepalive=alive) return self._cluster[iden].to_dict()
def get_delta_keepalive(self, delta: dt.timedelta): with self._lock: now = get_now() return [ cr.id for cr in self._cluster.values() if cr.birth is not None and cr.death is None and (now - (cr.keepalive or cr.birth)) < delta ]
def run_command_and_callback(operation: 'IOperationEncapsulation', params, context: Context, source: Server, step_execution: StepExecution, event_id, identity, timeout=None): execution: StepExecution = db.session.merge(step_execution) exec_id = execution.id source = db.session.merge(source) start = get_now() try: cp = operation.execute(params, timeout=timeout, context=context) except Exception as e: cp = CompletedProcess( success=False, stderr=f"Error while executing operation. {format_exception(e)}", start_time=start, end_time=get_now()) finally: execution.load_completed_result(cp) data = dict(step_execution=execution.to_json()) if execution.child_orch_execution: data['step_execution'].update( orch_execution=execution.child_orch_execution.to_json( add_step_exec=True)) # commit after data is dumped try: db.session.commit() except Exception as e: current_app.logger.exception( f"Error on commit for execution {exec_id}") resp, code = ntwrk.post(server=source, view_or_url='api_1_0.events', view_data={'event_id': event_id}, json=data, identity=identity) if code != 202: current_app.logger.error( f"Error while sending result for execution {exec_id}: {code}, {resp}" ) return data
def delete_old_temp_servers(): global servers_to_be_created now = get_now() for s_id, data in list(servers_to_be_created.items()): s_created = dt.datetime.strptime(data['created_on'], defaults.DATETIME_FORMAT) if now - s_created > dt.timedelta(hours=1): servers_to_be_created.pop(s_id, None)
def test_to_from_json(self): created = get_now() s2 = Step(orchestration=self.o, undo=True, stop_on_error=False, action_template=self.at2, expected_stdout='expected', expected_stderr='stderr', id='11111111-2222-3333-4444-111111110002', created_on=created) s1 = Step(orchestration=self.o, undo=True, stop_on_error=False, action_template=self.at1, expected_stdout='expected', expected_stderr='stderr', expected_rc=0, system_kwargs={'timeout': 180}, children_steps=[s2], id='11111111-2222-3333-4444-111111110001', created_on=created) s1_json = s1.to_json() s2_json = s2.to_json() self.assertDictEqual( dict(id='11111111-2222-3333-4444-111111110001', orchestration_id='11111111-2222-3333-4444-666666660001', undo=True, stop_on_error=False, action_template_id=str(self.at1.id), expected_stdout='expected', expected_stderr='stderr', parent_step_ids=[], expected_rc=0, system_kwargs={'timeout': 180}, created_on=created.strftime(defaults.DATETIME_FORMAT)), s1_json) self.assertDictEqual( dict(id='11111111-2222-3333-4444-111111110002', orchestration_id='11111111-2222-3333-4444-666666660001', undo=True, stop_on_error=False, action_template_id=str(self.at2.id), expected_stdout='expected', expected_stderr='stderr', parent_step_ids=['11111111-2222-3333-4444-111111110001'], system_kwargs={}, created_on=created.strftime(defaults.DATETIME_FORMAT)), s2_json) with self.assertRaises(errors.EntityNotFound): Step.from_json(s1_json) db.session.add(s1) smashed_s1 = Step.from_json(s1_json) self.assertEqual(s1, smashed_s1) s1_json['parent_step_ids'].append('11111111-2222-3333-4444-111111110003') with self.assertRaises(errors.EntityNotFound): smashed_s1 = Step.from_json(s1_json)
def ping(node): for n in node: dprint(f"### {n}:") if len(node) > 1 else None node_id = normalize2id(n) resp = ntwrk.post( 'root.ping', headers={'D-Destination': node_id}, json={'start_time': get_now().strftime(defaults.DATETIME_FORMAT)}) dprint(resp)
def create_step_execution(self, command): ident = str(uuid.uuid4()) with self.session_scope() as s: se = StepExecution(id=ident, step_id=command.id[1], server_id=command.id[0], orch_execution_id=self.json_orch_execution.get('id'), start_time=get_now()) s.add(se) self._store[ident] = se return ident
def store_string(self, string: str) -> None: # Save to file. with open(self.filename, "ab") as f: def write(t: str) -> None: f.write(t.encode("utf-8")) write("\n# %s\n" % get_now()) for line in string.split("\n"): write("%s+%s\n" % (self.tag, line))
def test_from_to_json_with_gate(self, mock_get_now, mock_uuid): mock_get_now.return_value = get_now() mock_uuid.return_value = '22cd859d-ee91-4079-a112-000000000002' s = Server('server2', id='22cd859d-ee91-4079-a112-000000000001') gate = mock.MagicMock() gate.to_json.return_value = {'server_id': 1, 'server': 'n1'} type(s).gates = mock.PropertyMock(return_value=[gate]) self.assertDictEqual( { 'id': '22cd859d-ee91-4079-a112-000000000001', 'name': 'server2', 'granules': [], 'gates': [{}], 'created_on': mock_get_now.return_value.strftime(defaults.DATETIME_FORMAT), '_old_name': None, 'deleted': False }, s.to_json(add_gates=True)) db.session.add(s) db.session.commit() s_json = s.to_json(add_gates=True) smashed = Server.from_json(s_json) self.assertIs(s, smashed) self.assertEqual(s.id, smashed.id) self.assertEqual(s.name, smashed.name) self.assertEqual(s.granules, smashed.granules) self.assertEqual(s.last_modified_at, smashed.last_modified_at) self.assertListEqual(s.gates, smashed.gates) # from new Server db.session.remove() db.drop_all() db.create_all() with patch('dimensigon.domain.entities.server.Gate.from_json' ) as mock_gate_from_json: smashed = Server.from_json(s_json) self.assertEqual(s.id, smashed.id) self.assertEqual(s.name, smashed.name) self.assertEqual(s.granules, smashed.granules) self.assertEqual(s.last_modified_at, smashed.last_modified_at) self.assertEqual(1, len(smashed.gates)) mock_gate_from_json.assert_called_once()
def upgrade_process(self): self.logger.debug("Starting check catalog from neighbours") # cluster information cluster_hearthbeat_id = get_now().strftime(defaults.DATETIME_FORMAT) # check version update before catalog update to match database revision data = asyncio.run( self._async_get_neighbour_healthcheck(cluster_hearthbeat_id)) if data: self.check_new_version(data) self.catalog_update(data) else: raise NoServerFound()
def receive_before_insert(mapper, connection, target): if not hasattr(catalog, 'data'): catalog.data = {} if getattr(catalog, 'datemark', True): target.last_modified_at = get_now() if not target.__class__ in catalog.data: catalog.data.update({target.__class__: target.last_modified_at}) else: catalog.data.update({ target.__class__: max(target.last_modified_at, catalog.data[target.__class__]) })
def test_to_from_json(self, mock_uuid, mock_get_now): now = get_now() mock_get_now.return_value = now mock_uuid.side_effect = [ '22cd859d-ee91-4079-a112-000000000001', '22cd859d-ee91-4079-a112-000000000002', '22cd859d-ee91-4079-a112-000000000003' ] s = Server('server', dns_or_ip='dns', gates=[('gdns', 6000)], created_on=now) self.assertDictEqual( { 'id': '22cd859d-ee91-4079-a112-000000000001', 'name': 'server', 'granules': [], 'created_on': now.strftime(defaults.DATETIME_FORMAT), 'deleted': False, '_old_name': None }, s.to_json()) self.assertDictEqual( { 'id': '22cd859d-ee91-4079-a112-000000000001', 'name': 'server', 'granules': [], 'created_on': now.strftime(defaults.DATETIME_FORMAT), 'ignore_on_lock': False, }, s.to_json(no_delete=True, add_ignore=True)) db.session.add(s) db.session.commit() db.session.remove() del s s = Server.query.get('22cd859d-ee91-4079-a112-000000000001') self.assertDictEqual( { 'id': '22cd859d-ee91-4079-a112-000000000001', 'name': 'server', 'granules': [], 'last_modified_at': now.strftime(defaults.DATEMARK_FORMAT), 'created_on': now.strftime(defaults.DATETIME_FORMAT), }, s.to_json(no_delete=True)) smashed = Server.from_json(s.to_json()) self.assertIs(s, smashed) self.assertEqual(s.id, smashed.id) self.assertEqual(s.name, smashed.name) self.assertEqual(s.granules, smashed.granules) self.assertEqual(s.last_modified_at, smashed.last_modified_at)
def test_equality(self): created = get_now() s1 = Step(orchestration=self.o, undo=True, stop_on_error=False, action_template=self.at1, expected_stdout='expected', expected_stderr='stderr', expected_rc=0, system_kwargs={'timeout': 30}, created_on=created) s2 = Step(orchestration=self.o, undo=True, stop_on_error=False, action_template=self.at2, expected_stdout='expected', expected_stderr='stderr', expected_rc=0, system_kwargs={'timeout': 30}, created_on=created) self.assertTrue(s1.eq_imp(s2)) s2.post_process = 'post_process' self.assertFalse(s1.eq_imp(s2))
def save_step_execution(self, command: ImplementationCommand, params=None, pre_process_time=None, execution_time=None, post_process_time=None): with self.session_scope() as s: se = s.merge(self._store[command.step_execution_id]) se.load_completed_result(command._cp) se.params = params se.pre_process_elapsed_time = pre_process_time se.execution_elapsed_time = execution_time se.post_process_elapsed_time = post_process_time se.end_time = get_now() if command._cp.stdout and ORCH_EXEC_PATTERN.match(command._cp.stdout): se.child_orch_execution_id = ORCH_EXEC_PATTERN.match(command._cp.stdout)[1]
def healthcheck(): if request.method == 'POST' and isinstance(g.source, Server): data = request.get_json() try: heartbeat = dt.datetime.strptime(data['heartbeat'], defaults.DATETIME_FORMAT) except: raise errors.InvalidDateFormat(data['heartbeat'], defaults.DATETIME_FORMAT) current_app.dm.cluster_manager.put(data['me'], heartbeat) catalog_ver = Catalog.max_catalog() data = { "version": dimensigon.__version__, "catalog_version": catalog_ver.strftime(defaults.DATEMARK_FORMAT) if catalog_ver else None, "services": [], } if not check_param_in_uri('human'): server = {'id': str(g.server.id), 'name': g.server.name} neighbours = [{ 'id': str(s.id), 'name': s.name } for s in Server.get_neighbours()] cluster = { 'alive': current_app.dm.cluster_manager.get_alive(), 'in_coma': current_app.dm.cluster_manager.get_zombies() } else: server = g.server.name neighbours = sorted([s.name for s in Server.get_neighbours()]) cluster = { 'alive': sorted([ getattr(Server.query.get(i), 'name', i) for i in current_app.dm.cluster_manager.get_alive() ]), 'in_coma': sorted([ getattr(Server.query.get(i), 'name', i) for i in current_app.dm.cluster_manager.get_zombies() ]) } data.update(server=server, neighbours=neighbours, cluster=cluster, now=get_now().strftime(defaults.DATETIME_FORMAT)) return data
def _ping(dest: t.Union[Server, Gate], source: Server, retries=None, timeout=None, verify=False): tries = 0 cost = None elapsed = None exc = None if isinstance(dest, Gate): server = dest.server try: schema = current_app.config['PREFERRED_URL_SCHEME'] or 'https' except: schema = 'https' url = f"{schema}://{dest}/{url_for('root.ping', _external=False)}" else: server = dest try: url = dest.url('root.ping') except: return None, None while tries < retries: try: tries += 1 resp = requests.post(url, json={ 'start_time': get_now().strftime( defaults.DATETIME_FORMAT) }, headers={ 'D-Source': str(source.id), 'D-Destination': str(server.id) }, verify=verify, timeout=timeout) except requests.exceptions.ReadTimeout as e: resp = None exc = e except requests.exceptions.ConnectionError as e: # unable to reach actual server through current gateway resp = None exc = e if resp is not None and resp.status_code == 200: cost = len(resp.json().get('servers', {})) elapsed = resp.elapsed tries = retries return cost, elapsed
def fetch_catalog(data_mark=None): data = {} now = get_now() for name, obj in get_distributed_entities(): c = Catalog.query.get(name) # db.session.query to bypass deleted objects to spread deleted changes if data_mark: query = obj.query.filter(obj.last_modified_at > data_mark) else: query = obj.query repo_data = query.filter(obj.last_modified_at <= now).all() if name == 'User': data.update({name: [e.to_json(password=True) for e in repo_data]}) else: data.update({name: [e.to_json() for e in repo_data]}) return data
def set_keepalive(self, iden: Id, keepalive=None) -> t.Optional[t.Dict]: with self._lock: keepalive = keepalive or get_now() initial_cr = copy.deepcopy(self._cluster.get(iden, None)) if iden in self._cluster: if self._cluster[iden].death is not None: self.set_alive(iden) if not self._cluster[iden].keepalive or self._cluster[ iden].keepalive < keepalive - self.threshold: self._cluster[iden].keepalive = keepalive else: self._cluster[iden] = self._register_class(iden, birth=keepalive, keepalive=keepalive) return self._cluster[iden].to_dict( ) if self._cluster[iden] != initial_cr else None
def set_keepalive(self, iden: Id, session: Id = None, keepalive=None) -> t.Optional[t.Dict]: with self._lock: keepalive = keepalive or get_now() if iden in self._cluster: if self._cluster[iden].session == session: self.set_alive(iden, session, keepalive) self._cluster[iden].keepalive = keepalive else: self._cluster[iden] = self._register_class(iden, session=session, birth=keepalive, keepalive=keepalive) return self._cluster[iden].to_dict()