def cb_init(self, tctx): if self.maapi is None: self.maapi = Maapi() self.trans = self.maapi.attach(tctx) name = 'th-{0}'.format(tctx.th) wsock = take_worker_socket(self.daemon, name, self._make_key(tctx)) try: init_cb = getattr(self, 'init', None) if callable(init_cb): init_cb(tctx) # Associate worker socket with the transaction trans_set_fd(tctx, wsock) except Exception as e: return_worker_socket(self.daemon, self._make_key(tctx)) raise
def cb_action(self, uinfo, name, kp, input, output): def iterate(keypath, op, old_value, new_value): if op == ncs.MOP_DELETED and len(keypath) > 2 and kp_value(keypath[0]) == 'commit-queue': # /ncs:services/cq-notify-test:c-queue-test1/vrf{cust4}/plan/commit-queue self.log.info("Commit-queue completed for notifier {}, service instance {}.".format( input.kicker_id, kp_value(keypath[2]) )) send_rest_notification(service=kp_value(keypath[2]), notifier=input.kicker_id) return ncs.ITER_STOP else: return ncs.ITER_RECURSE def send_rest_notification(**kwargs): # Retrieve configured REST parameters with ncs.maapi.single_read_trans(uinfo.username, "system") as read_t: rest_params = ncs.maagic.get_node(read_t, kp).rest uri = rest_params.uri source_data = rest_params.payload rest_user = rest_params.username read_t.maapi.install_crypto_keys() rest_pass = _ncs.decrypt(rest_params.password) # Process source_data through jinja2 jinja_env = jinja2.Environment(autoescape=True, trim_blocks=True, lstrip_blocks=True) data = jinja_env.from_string(source_data).render(kwargs) # Send REST notification try: response = requests.post(url=uri, auth=(rest_user, rest_pass), data=data) response.raise_for_status() self.log.info("REST notification sent successfully: status code {}.".format(response.status_code)) except requests.exceptions.RequestException as e: self.log.error("REST notification failed: {}".format(e)) with Maapi() as m: try: t = m.attach(input.tid) t.diff_iterate(iterate, ncs.ITER_WANT_P_CONTAINER) finally: m.detach(input.tid)
def cb_action(self, uinfo, name, kp, input, output): def iterate(keypath, op, old_value, new_value): if op == ncs.MOP_CREATED and len(keypath) > 3 and str(keypath[0]) == 'request': # /loopback:external-resource-manager/id-pool{pool-1}/allocation{XR-0-1}/request allocation_id = kp_value(keypath[1]) pool_name = kp_value(keypath[3]) assigned_id = id_allocator.allocate(allocation_id, pool_name) if assigned_id is None: self.log.error('Resource pool {} exhausted'.format(pool_name)) else: with ncs.maapi.single_write_trans(uinfo.username, "system", db=ncs.OPERATIONAL) as write_t: ncs.maagic.get_node(write_t, keypath)._parent.response.assigned_id = assigned_id write_t.apply() self.log.info('Allocated: {}, {}, value: {}'.format(pool_name, allocation_id, assigned_id)) elif op == ncs.MOP_DELETED and len(keypath) > 2 and str(keypath[1]) == 'allocation': # /loopback:external-resource-manager/id-pool{pool-1}/allocation{XR-0-1} allocation_id = kp_value(keypath[0]) pool_name = kp_value(keypath[2]) diff_size = id_allocator.deallocate(allocation_id, pool_name) self.log.info('De-allocated: {}, {}, {} changes'.format(pool_name, allocation_id, diff_size)) else: return ncs.ITER_RECURSE return ncs.ITER_STOP self.log.info('Action input: kicker-id: {}, path: {}, tid: {}'.format(input.kicker_id, input.path, input.tid)) id_allocator = FileAllocator(Config.ID_POOL) with Maapi() as m: try: t = m.attach(input.tid) t.diff_iterate(iterate, ncs.ITER_WANT_P_CONTAINER) finally: m.detach(input.tid)
class Validation(object): """ Convenience class for custom validation callbacks 1) Create a custom validation class extending Validation Your custom validation class needs to implement the validate method. Optionally it can have init and stop methods, which get called at the beginning and end of the transaction validation phase. class CustomValidation(Validation): def validate(self, tctx, kp, newval, root): self.log.info('validate a called') return ncs.CONFD_OK def init(self, tctx): self.log.info('custom init a called') def stop(self, tctx): self.log.info('custom stop a called') 2) Decorate your application class with custom_validators and register your custom validation class: The decorator allows calling self.register_validation within the setup method in a similar way as the builtin register_service and register_action methods. @Validation.custom_validators class Main(ncs.application.Application): def setup(self): ... self.register_validation('custom-validate-a', CustomValidation) ... """ @staticmethod def custom_validators(cls): if not issubclass(cls, ncs.application.Application): raise TypeError( 'custom_validators can only decorate ncs.application.Application subclasses' ) original_init = cls.__init__ original_setup = cls.setup original_teardown = cls.teardown def __init__(self, *args, **kwargs): original_init(self, *args, **kwargs) self._daemons = [] def setup(self): r = original_setup(self) for daemon in self._daemons: daemon.start() return r def teardown(self): r = original_teardown(self) for daemon in self._daemons: daemon.finish() return r def register_validation(self, validation_point, validation_cls): daemon = Daemon('ncs-dp-{0}-{1}:{2}'.format( os.getpid(), self._ncs_pname, validation_point), log=self.log) v = validation_cls(daemon, validation_point) register_trans_validate_cb(daemon.ctx(), v) register_valpoint_cb(daemon.ctx(), validation_point, v) self._daemons.append(daemon) cls.__init__ = __init__ cls.setup = setup cls.teardown = teardown cls.register_validation = register_validation return cls def __init__(self, daemon, validation_point): self.validation_point = validation_point self.log = daemon.log self.maapi = None self.trans = None self.daemon = daemon def cb_init(self, tctx): if self.maapi is None: self.maapi = Maapi() self.trans = self.maapi.attach(tctx) name = 'th-{0}'.format(tctx.th) wsock = take_worker_socket(self.daemon, name, self._make_key(tctx)) try: init_cb = getattr(self, 'init', None) if callable(init_cb): init_cb(tctx) # Associate worker socket with the transaction trans_set_fd(tctx, wsock) except Exception as e: return_worker_socket(self.daemon, self._make_key(tctx)) raise def cb_stop(self, tctx): try: stop_cb = getattr(self, 'stop', None) if callable(stop_cb): stop_cb(tctx) finally: try: self.maapi.detach(tctx) except Exception as e: pass self.trans = None return_worker_socket(self.daemon, self._make_key(tctx)) def cb_validate(self, tctx, kp, newval): validate_cb = getattr(self, 'validate', None) if callable(validate_cb): root = ncs.maagic.get_root(self.trans, shared=False) return validate_cb(tctx, kp, newval, root) raise NotImplementedError() def _make_key(self, tctx): return '{0}-{1}'.format(id(self), tctx.th)