diff --git a/nso-nipap/nso-nipap/python/nso_nipap/__init__.py b/nso-nipap/nso-nipap/python/nso_nipap/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/nso-nipap/nso-nipap/python/nso_nipap/prefix_allocator.py b/nso-nipap/nso-nipap/python/nso_nipap/prefix_allocator.py new file mode 100644 index 000000000..b1faa8bf5 --- /dev/null +++ b/nso-nipap/nso-nipap/python/nso_nipap/prefix_allocator.py @@ -0,0 +1,195 @@ +import logging + +import ncs +import ncs.maapi as maapi +import ncs.maagic as maagic + +log = logging.getLogger() + + +def prefix_request(service, svc_xpath, pool_name, allocation_name, + prefix_length, family=4, prefix_attributes=None): + """ Create a prefix allocation request + + Arguments: + service -- the requesting service node + svc_xpath -- xpath to the requesting service + pool_name -- name of pool to request from + allocation_name -- unique allocation name + prefix_length -- the prefix length of the allocated network + family -- address family of the network, 4 (IPv4) or 6 (IPv6) + prefix_attributes -- dict with prefix attributes + """ + + if prefix_attributes is None: + prefix_attributes = {} + + template = ncs.template.Template(service) + vars = ncs.template.Variables() + + # required variables + vars.add("POOL_NAME", pool_name) + vars.add("ALLOCATION_NAME", allocation_name) + vars.add("SERVICE", svc_xpath) + vars.add("PREFIX_LENGTH", prefix_length) + vars.add("FAMILY", family) + + # optional prefix attributes + _set_prefix_attributes(prefix_attributes, vars) + + log.debug("Placing prefix request with data %s" % vars) + + template.apply('nso-nipap-prefix-request', vars) + + +def from_prefix_request(service, pool_name, main_allocation_name, + from_pref_allocation_name, prefix_attributes=None): + """ Create a from-prefix allocation request + + Arguments: + service -- the requesting service node + pool_name -- name of pool to request from + main_allocation_name -- name of main allocation which the from-prefix is appended to + from_pref_allocation_name -- name of from-prefix allocation + prefix_attributes -- dict with prefix attributes + """ + + if prefix_attributes is None: + prefix_attributes = {} + + template = ncs.template.Template(service) + vars = ncs.template.Variables() + + # required variables + vars.add('POOL_NAME', pool_name) + vars.add('ALLOCATION_NAME', main_allocation_name) + vars.add('FROM_PREFIX_ALLOCATION_NAME', from_pref_allocation_name) + + # optional prefix attributes + _set_prefix_attributes(prefix_attributes, vars) + + log.debug("Placing from-prefix request with data %s" % vars) + + template.apply('nso-nipap-from-prefix-request', vars) + + +def prefix_read(root, pool_name, allocation_name): + """Returns the allocated network or None + + Arguments: + root -- a maagic root for the current transaction + pool_name -- name of pool to request from + allocation_name -- unique allocation name + """ + # Look in the current transaction + _verify_allocation(root, pool_name, allocation_name) + + # Now we switch from the current trans to actually see if + # we have received the alloc + with maapi.single_read_trans("admin", "system", + db=ncs.OPERATIONAL) as th: + + oper_root = maagic.get_root(th) + alloc = _get_allocation(oper_root, pool_name, allocation_name) + if alloc is None: + return None + + if alloc.response.prefix: + return alloc.response.prefix + else: + return None + + +def from_prefix_read(root, pool_name, main_allocation_name, from_prefix_allocation_name): + """Returns the allocated network or None + + Arguments: + root -- a maagic root for the current transaction + pool_name -- name of pool to request from + main_allocation_name -- name of allocation which the from-prefix allocation belongs to + from_prefix_allocation_name -- name of from-prefix allocation + """ + # Look in the current transaction + alloc = _verify_allocation(root, pool_name, main_allocation_name) + if from_prefix_allocation_name not in alloc.from_prefix_request: + raise LookupError("from-prefix allocation %s does not exist in main allocation %s from pool %s" % + (from_prefix_allocation_name, main_allocation_name, pool_name)) + + # Now we switch from the current trans to actually see if + # we have received the alloc + with maapi.single_read_trans("admin", "system", + db=ncs.OPERATIONAL) as th: + oper_root = maagic.get_root(th) + alloc = _get_allocation(oper_root, pool_name, main_allocation_name) + if alloc is None: + return None + + if from_prefix_allocation_name not in alloc.from_prefix_request: + return None + + from_pref_alloc = alloc.from_prefix_request[from_prefix_allocation_name] + + if from_pref_alloc.response.prefix: + return from_pref_alloc.response.prefix + else: + return None + + +def _set_prefix_attributes(attributes, template_vars): + """ Fetch prefix attributes from CDB and write to template vars + """ + + template_vars.add('CUSTOMER_ID', + attributes['customer_id'] if 'customer_id' in attributes else '-1') + + template_vars.add('DESCRIPTION', + attributes['description'] if 'description' in attributes else '-1') + + template_vars.add('NODE', + attributes['node'] if 'node' in attributes else '-1') + + template_vars.add('ORDER_ID', + attributes['order_id'] if 'order_id' in attributes else '-1') + + +def _verify_allocation(root, pool_name, allocation_name): + """ Verify that the allocation exists and return it + + Throws LookupError if allocation is missing. + """ + pool_l = root.ncs__services.nipap__nipap.from_pool + + if pool_name not in pool_l: + raise LookupError("Pool %s does not exist" % (pool_name)) + + pool = pool_l[pool_name] + + if allocation_name not in pool.request: + raise LookupError("allocation %s does not exist in pool %s" % + (allocation_name, pool_name)) + + return pool.request[allocation_name] + + +def _get_allocation(root, pool_name, allocation_name): + """ Return allocation. + + Returns None if allocation does not exist, raises exception if + allocation status == 'error'. + """ + alloc = None + try: + alloc = _verify_allocation(root, pool_name, allocation_name) + except LookupError as e: + return None + + if alloc.response.status == 'ok': + return alloc + elif alloc.response.status == 'error': + raise AllocationError(alloc.response.status_message) + + +class AllocationError(Exception): + """ Exception thrown when allocation has failed. + """ + pass diff --git a/nso-nipap/nso-nipap/src/java/src/net/spritelink/nsonipap/ConfigCdbSub.java b/nso-nipap/nso-nipap/src/java/src/net/spritelink/nsonipap/ConfigCdbSub.java index c517dd302..935e3cd80 100644 --- a/nso-nipap/nso-nipap/src/java/src/net/spritelink/nsonipap/ConfigCdbSub.java +++ b/nso-nipap/nso-nipap/src/java/src/net/spritelink/nsonipap/ConfigCdbSub.java @@ -3,7 +3,7 @@ import net.spritelink.nsonipap.namespaces.*; import java.util.*; -import java.math.BigInteger; +import java.io.IOException; import org.apache.log4j.Logger; @@ -30,28 +30,25 @@ public class ConfigCdbSub implements ApplicationComponent { private CdbSubscription sub = null; private CdbSession wsess; - private CdbSession rsess; public ConfigCdbSub() { } @Resource(type=ResourceType.CDB, scope=Scope.CONTEXT, - qualifier="reactive-fm-loop-subscriber") + qualifier="reactive-fm-loop-subscriber") private Cdb cdb; @Resource(type=ResourceType.CDB, scope=Scope.CONTEXT, - qualifier="w-reactive-fm-loop") + qualifier="w-reactive-fm-loop") private Cdb wcdb; @Resource(type=ResourceType.MAAPI, scope=Scope.INSTANCE, - qualifier="reactive-fm-m") + qualifier="reactive-fm-m") private Maapi maapi; - private int th = -1; - private NavuContainer ncsRoot; - private NavuContainer operRoot; + private int th = -1; - private Connection nipapCon; + private Connection nipapCon; public void init() { LOGGER.info("Starting the CDB Connection..."); @@ -62,25 +59,285 @@ public void init() { new String[] {"admin"}, MaapiUserSessionFlag.PROTO_TCP); - th = maapi.startTrans(Conf.DB_RUNNING, Conf.MODE_READ); - NavuContainer root = new NavuContainer(new NavuContext(maapi, th)); - ncsRoot = root.container(Ncs.hash); - NavuContainer cdbRoot = new NavuContainer(new NavuContext(cdb)); - NavuContainer operRoot = cdbRoot.container(Ncs.hash); + th = maapi.startTrans(Conf.DB_RUNNING, Conf.MODE_READ); sub = cdb.newSubscription(); - sub.subscribe(1, new nipap(), "/services/nipap/pool/request"); + + sub.subscribe(1, new nipap(), + "/services/" + + nipap.prefix + "/" + + nipap._from_pool_ + "/" + + nipap._request_); + // Tell CDB we are ready for notifications sub.subscribeDone(); - } - catch (Exception e) { + } catch (Exception e) { LOGGER.error("", e); } } + /** + * Add a host prefix from a prefix. + * + * @param path Path to the prefix request + * @param parentPrefix Prefix to get host prefix from + * @throws ConfException + * @throws IOException + */ + protected void addHostPrefixFromPrefix(String path, Prefix parentPrefix) throws ConfException, IOException { + + LOGGER.info("Create, From prefix request, path = " + path); + + AddPrefixOptions child_opts = new AddPrefixOptions(); + if (parentPrefix.family.intValue() == 4) { + child_opts.put("prefix_length", "32"); + } else { + child_opts.put("prefix_length", "128"); + } + + Prefix child_prefix = new Prefix(); + child_prefix = getPrefixAttributesFromCDB(path + "/" + nipap._attributes_ ); + child_prefix.type = "host"; + + try { + child_prefix.save(nipapCon, parentPrefix, child_opts); + } catch (JnipapException e) { + LOGGER.error("Unable to get prefix from prefix" + e.getMessage(), e); + writeError(path, e.getMessage()); + return; + } + + // write response + writeResponseToCDB(child_prefix, path + "/" + nipap._response_); + + } + + /** + * Fetch attributes and returns the populated prefix object + * + * @param attributePath Path to the prefix attribute container + * @return Prefix + * @throws ConfException + * @throws IOException + */ + protected Prefix getPrefixAttributesFromCDB(String attributePath) throws ConfException, IOException { + + Prefix p = new Prefix(); + + return getPrefixAttributesFromCDB(p, attributePath); + } + + /** + * Fetch attributes and returns the populated prefix object + * + * @param basePrefix Used if you already have a prefix object + * @param attributePath Path to the prefix attribute container + * @return Prefix + * @throws ConfException + * @throws IOException + */ + protected Prefix getPrefixAttributesFromCDB(Prefix basePrefix, String attributePath) throws ConfException, IOException { + + Prefix p = basePrefix; + + if (maapi.exists(th, attributePath + "/" + nipap._customer_id_)) { + ConfValue rCustomer_id = maapi.getElem(th, attributePath + "/" + nipap._customer_id_); + p.customer_id = String.valueOf(rCustomer_id); + } else { + p.customer_id = null; + } + if (maapi.exists(th, attributePath + "/" + nipap._description_)) { + ConfValue rDescription = maapi.getElem(th, attributePath + "/" + nipap._description_); + p.description = String.valueOf(rDescription); + } else { + p.description = null; + } + if (maapi.exists(th, attributePath + "/" + nipap._node_)) { + ConfValue rNode = maapi.getElem(th, attributePath + "/" + nipap._node_); + p.node = String.valueOf(rNode); + } else { + p.node = null; + } + if (maapi.exists(th, attributePath + "/" + nipap._order_id_)) { + ConfValue rOrder_id = maapi.getElem(th, attributePath + "/" + nipap._customer_id_); + p.order_id = String.valueOf(rOrder_id); + } else { + p.order_id = null; + } + + return p; + } + + /** + * Fetch prefix options from CDB + * + * @param argumentPath Path to prefix argument container + * @return AddPrefixOptions + * @throws ConfException + * @throws IOException + */ + protected AddPrefixOptions getPrefixOptionsFromCDB (String argumentPath) throws ConfException, IOException { + AddPrefixOptions opts = new AddPrefixOptions(); + + ConfEnumeration family = (ConfEnumeration)maapi.getElem( + th, argumentPath + "/" + nipap._family_); + opts.put("family", family.getOrdinalValue()); + + if (maapi.exists(th, argumentPath + "/" + nipap._prefix_length_)) { + ConfValue pfx_length = maapi.getElem(th, argumentPath + "/" + nipap._prefix_length_); + opts.put("prefix_length", String.valueOf(pfx_length)); + } + + return opts; + } + + /** + * Write response data to CDB oper + * + * @param prefix Prefix object to write + * @param responsePath Path were the response should be written + * @throws ConfException + * @throws IOException + */ + protected void writeResponseToCDB(Prefix prefix, String responsePath) throws ConfException, IOException { + + if (prefix.family == 4) { + ConfIPv4Prefix prefixValue = new ConfIPv4Prefix(prefix.prefix); + LOGGER.info("SET: " + responsePath + "/prefix -> " + prefixValue); + wsess.setElem(prefixValue, responsePath + "/" + nipap._prefix_); + } else if (prefix.family == 6) { + + ConfIPv6Prefix prefixValue = new ConfIPv6Prefix(prefix.prefix); + LOGGER.info("SET: " + responsePath + "/prefix -> " + prefixValue); + wsess.setElem(prefixValue, responsePath + "/" + nipap._prefix_); + } + + ConfUInt32 prefixIdValue = new ConfUInt32(prefix.id); + wsess.setElem(prefixIdValue, responsePath + "/" + nipap._prefix_id_); + + if (prefix.customer_id != null){ + ConfBuf customerIdValue = new ConfBuf(prefix.customer_id); + wsess.setElem(customerIdValue, responsePath + "/" + nipap._customer_id_); + } + + if (prefix.description != null){ + ConfBuf descriptionValue = new ConfBuf(prefix.description); + wsess.setElem(descriptionValue, responsePath + "/" + nipap._description_); + } + + if (prefix.node != null){ + ConfBuf nodeValue = new ConfBuf(prefix.node); + wsess.setElem(nodeValue, responsePath + "/" + nipap._node_); + } + + if (prefix.order_id != null){ + ConfBuf orderIdValue = new ConfBuf(prefix.order_id); + wsess.setElem(orderIdValue, responsePath + "/" + nipap._order_id_); + } + wsess.setElem(ConfEnumeration.getEnumByLabel( responsePath + "/" + nipap._status_, "ok"), + responsePath + "/" + nipap._status_); + } + + /** + * Write error message + * + * @param path Path to request + * @param errorMessage Error message + * @throws ConfException + * @throws IOException + */ + protected void writeError(String path, String errorMessage) throws ConfException, IOException { + ConfEnumeration error = ConfEnumeration.getEnumByLabel(path + "/" + nipap._response_ + "/" + nipap._status_, "error"); + + wsess.setElem(new ConfBuf(errorMessage), path + "/" + nipap._response_ + "/" + nipap._status_message_); + wsess.setElem(error , path + "/" + nipap._response_ + "/" + nipap._status_); + + } + + /** + * Remove response from CDB oper. + * TODO: Do we need to do this? + * + * @param responsePath path to response + * @throws ConfException + * @throws IOException + */ + protected void removeResponseFromCDB(String responsePath) throws ConfException, IOException { + //unset case + + LOGGER.info("remove response " + responsePath); + try { + wsess.delete(responsePath + "/" + nipap._prefix_); + wsess.delete(responsePath + "/" + nipap._prefix_id_); + wsess.delete(responsePath + "/" + nipap._customer_id_); + wsess.delete(responsePath + "/" + nipap._description_); + wsess.delete(responsePath + "/" + nipap._node_); + wsess.delete(responsePath + "/" + nipap._order_id_); + } catch (CdbException e ) { + } + } + + /** + * Update NIPAP with the new prefix information + * + * @param prefixPath Path to prefix request + * @throws ConfException + * @throws IOException + * @throws JnipapException for NIPAP related issues + */ + protected void updatePrefixInNIPAP(String prefixPath) throws ConfException, IOException, JnipapException { + + LOGGER.info("Update prefix: " + prefixPath); + String responsePath = prefixPath + "/" + nipap._response_; + + int p_id = getPrefixId(responsePath); + + Prefix p = Prefix.get(nipapCon, p_id); + + Prefix newPrefix = getPrefixAttributesFromCDB(p, prefixPath + "/" + nipap._attributes_); + + newPrefix.save(nipapCon); + writeResponseToCDB(newPrefix, responsePath); + } + + /** + * Remove Prefix from NIPAP + * + * @param prefixPath Path to Prefix + * @throws ConfException + * @throws IOException + * @throws JnipapException for NIPAP related issues + */ + protected void removePrefixFromNIPAP(String prefixPath) throws ConfException, IOException, JnipapException { + + int p_id = getPrefixId(prefixPath); + LOGGER.info("Removing prefix ID: " + p_id); + + try { + Prefix p = Prefix.get(nipapCon, p_id); + p.remove(nipapCon, true); + } catch (JnipapException e) { + LOGGER.error("Unable to remove prefix from NIPAP: " + e.getMessage(),e); + throw e; + } + } + + /** + * Get prefix ID + * + * @param path Path to prefix response + * @return Prefix ID + * @throws ConfException + * @throws IOException + */ + protected int getPrefixId(String path) throws ConfException, IOException { + return (int) ((ConfUInt32)wsess.getElem(path + "/" + + nipap._prefix_id_)).longValue(); + } + public void run() { - LOGGER.info("Starting the CDB subscriber..."); + LOGGER.info("Starting the CDB subscriber..."); try { while(true) { @@ -95,10 +352,10 @@ public void run() { } // DiffIterateFlags tell our DiffIterator implementation what values we want EnumSet enumSet = - EnumSet.of( - DiffIterateFlags.ITER_WANT_PREV, - DiffIterateFlags.ITER_WANT_ANCESTOR_DELETE, - DiffIterateFlags.ITER_WANT_SCHEMA_ORDER); + EnumSet.of( + DiffIterateFlags.ITER_WANT_PREV, + DiffIterateFlags.ITER_WANT_ANCESTOR_DELETE, + DiffIterateFlags.ITER_WANT_SCHEMA_ORDER); ArrayList reqs = new ArrayList(); try { // Iterate through the diff tree using the Iter class @@ -106,8 +363,7 @@ public void run() { sub.diffIterate(points[0], new Iter(sub), enumSet, reqs); - } - catch (Exception e) { + } catch (Exception e) { reqs = null; } @@ -115,102 +371,201 @@ public void run() { for (Request req : reqs) { LOGGER.debug("Requested NIPAP action, op=" + req.op + " , type=" + req.t); - try { - // TODO: make backend configurable (now it is 'default') - ConfValue bHost = maapi.getElem(th, "/services/nipap/backend{default}/hostname"); - ConfValue bPort = maapi.getElem(th, "/services/nipap/backend{default}/port"); - ConfValue bUser = maapi.getElem(th, "/services/nipap/backend{default}/username"); - ConfValue bPass = maapi.getElem(th, "/services/nipap/backend{default}/password"); - - URL url = new URL("http://" + String.valueOf(bHost) + ":" + String.valueOf(bPort) + "/RPC2"); - nipapCon = new Connection(url, String.valueOf(bUser), String.valueOf(bPass)); - nipapCon.authoritative_source = "ncs"; - - } catch (Exception e) { - LOGGER.error("Unable to initiate connection to NIPAP: " + e.getMessage()); - continue; - } - - - // allocate prefix - if ((req.op == Operation.ALLOCATE) && - (req.t == Type.Prefix)) { - - LOGGER.info("Trying to allocate a prefix for: " + req.request_key + " from pool: " + req.pool_key); - String poolName = String.valueOf(req.pool_key).replaceAll("[{}]", ""); - - Prefix p = new Prefix(); - // Gather prefix data and perform NIPAP request - try { - // Pool - HashMap poolSpec = new HashMap(); - poolSpec.put("name", poolName); - List poolRes = Pool.list(nipapCon, poolSpec); - - // options, like address-family - AddPrefixOptions opts = new AddPrefixOptions(); - ConfEnumeration family = (ConfEnumeration)maapi.getElem(th, req.path + "/family"); - opts.put("family", family.getOrdinalValue()); - - ConfValue rDescription = maapi.getElem(th, req.path + "/description"); - if (rDescription != null) - p.description = String.valueOf(rDescription); - - ConfValue rNode = maapi.getElem(th, req.path + "/node"); - if (rNode != null) - p.node = String.valueOf(rNode); - - p.save(nipapCon, (Pool)poolRes.get(0), opts); - - } catch (Exception e) { - LOGGER.error("Unable to get prefix from NIPAP: " + e.getMessage(), e); - continue; - } - - // Write the result - if (p.family == 4) { - ConfIPv4Prefix prefixValue = new ConfIPv4Prefix(p.prefix); - LOGGER.info("SET: " + req.path + "/prefix -> " + prefixValue); - wsess.setElem(prefixValue, req.path + "/prefix"); - } else if (p.family == 6) { - ConfIPv6Prefix prefixValue = new ConfIPv6Prefix(p.prefix); - LOGGER.info("SET: " + req.path + "/prefix -> " + prefixValue); - wsess.setElem(prefixValue, req.path + "/prefix"); - } - ConfUInt64 prefixIdValue = new ConfUInt64(p.id); - wsess.setElem(prefixIdValue, req.path + "/prefix_id"); - - // Redeploy - try { - ConfValue redeployPath = maapi.getElem(th, req.path + "/redeploy-service"); - LOGGER.info("redeploy-service: " + redeployPath); - redeploy(redeployPath.toString()); - } catch (Exception e) { - LOGGER.error("Redeploy failed: " + e.getMessage()); - } - } - - else if (req.op == Operation.DEALLOCATE && - (req.t == Type.Prefix)) { - //Deallocate prefix + try { try { - ConfUInt64 p_id = (ConfUInt64)wsess.getElem(req.path + "/prefix_id"); - LOGGER.info("Removing prefix ID: " + p_id); - Prefix p = Prefix.get(nipapCon, Integer.parseInt(String.valueOf(p_id))); - p.remove(nipapCon); - wsess.delete(req.path + "/prefix_id"); - wsess.delete(req.path + "/prefix"); + // TODO: make backend configurable (now it is 'default') + ConfValue bHost = maapi.getElem(th, "/services/nipap/backend{default}/hostname"); + ConfValue bPort = maapi.getElem(th, "/services/nipap/backend{default}/port"); + ConfValue bUser = maapi.getElem(th, "/services/nipap/backend{default}/username"); + ConfValue bPass = maapi.getElem(th, "/services/nipap/backend{default}/password"); + + URL url = new URL("http://" + String.valueOf(bHost) + ":" + String.valueOf(bPort) + "/RPC2"); + nipapCon = new Connection(url, String.valueOf(bUser), String.valueOf(bPass)); + nipapCon.authoritative_source = "ncs"; + } catch (Exception e) { - LOGGER.error("Unable to remove prefix from NIPAP: " + e.getMessage(),e); - continue; + String estr = "Unable to initiate connection to NIPAP: " + e.getMessage(); + LOGGER.error(estr); + writeError(req.path.toString(), estr); } - } + /* + * Allocate new Prefix + * + */ + if (req.op == Operation.ALLOCATE && req.t == Type.Request ) { + + LOGGER.info("Trying to allocate a prefix for: " + req.request_key + " from pool: " + req.pool_key); + + String poolName = String.valueOf(req.pool_key).replaceAll("[{}]", ""); + + Prefix p = new Prefix(); + // Gather prefix data and perform NIPAP request + try { + // Pool + HashMap poolSpec = new HashMap<>(); + poolSpec.put("name", poolName); + List poolRes = Pool.list(nipapCon, poolSpec); + + if (poolRes.size() != 1) { + writeError(req.path.toString(), "Pool " + poolName + " not found in NIPAP"); + continue; + } + + // options, like address-family + AddPrefixOptions opts = getPrefixOptionsFromCDB(req.path + "/" + nipap._arguments_); + + // set prefix attributes + String attrPath = req.path + "/" + nipap._attributes_; + p = getPrefixAttributesFromCDB(attrPath); + + p.save(nipapCon, (Pool)poolRes.get(0), opts); + + } catch (JnipapException e) { + LOGGER.error("Unable to get prefix from NIPAP: " + e.getMessage(), e); + writeError(req.path.toString(), e.getMessage()); + continue; + } + + // Write the result + String resPath = req.path + "/" + nipap._response_; + writeResponseToCDB(p, resPath); + + + // Request prefix from prefix + String fromPrefixPath = req.path + "/" + nipap._from_prefix_request_; + if (maapi.exists(th, fromPrefixPath)){ + MaapiCursor pfx_cur = maapi.newCursor(th, fromPrefixPath); + ConfKey pfx = null; + + while((pfx = maapi.getNext(pfx_cur)) != null) { + addHostPrefixFromPrefix(fromPrefixPath + pfx, p); + } + } + + // Redeploy + try { + ConfObjectRef v = (ConfObjectRef)maapi.getElem(th, req.path + "/redeploy-service"); + ConfPath redeployPath = new ConfPath(v.getElems()); + LOGGER.info("redeploy-service: " + redeployPath); + redeploy(redeployPath); + } catch (Exception e) { + LOGGER.error("Redeploy failed: " + e.getMessage()); + } + } + /* + * Allocate from-prefix + * + */ + else if (req.op == Operation.ALLOCATE && req.t == Type.FromPrefixRequest){ + LOGGER.info("Create, From prefix request"); + + String path = "/" + Ncs._services_ + "/" + nipap.prefix + ":" + nipap.prefix + "/" + + nipap._from_pool_ + req.pool_key + "/" + nipap._request_ + req.request_key; + + try { + int p_id = getPrefixId(path + "/" + nipap._response_ ); + + Prefix parentPrefix = Prefix.get(nipapCon, p_id); + + addHostPrefixFromPrefix(path + "/" + nipap._from_prefix_request_ + req.prefix_key, parentPrefix); + } catch (JnipapException e) { + String estr = "Allocating from-prefix failed: " + e.getMessage(); + LOGGER.error(estr); + writeError(req.path.toString(), estr); + continue; + } + } + /* + * Deallocate Prefix + * + */ + else if (req.op == Operation.DEALLOCATE && (req.t == Type.Request)) { + + String path = req.path + "/" + nipap._response_; + + LOGGER.info("Deallocate Prefix (" + path + ")"); + + try { + if(maapi.exists(th, path + "/" + nipap._prefix_)){ + removePrefixFromNIPAP(path); + } + + removeResponseFromCDB(path); + } catch (JnipapException e) { + String estr = "Deallocation of prefix failed: " + e.getMessage(); + LOGGER.error(estr); + writeError(req.path.toString(), estr); + continue; + } + } + /* + * Deallocate from-prefix prefix + * + */ + else if (req.op == Operation.DEALLOCATE && (req.t == Type.FromPrefixRequest)) { - } + String path = req.path + "/" + nipap._response_; + + LOGGER.info("Deallocate Prefix (" + path + ")"); + + try { + if(maapi.exists(th, path + "/" + nipap._prefix_)){ + removePrefixFromNIPAP(path); + } + + removeResponseFromCDB(path); + } catch (JnipapException e) { + String estr = "Deallocation of from-prefix failed: " + e.getMessage(); + LOGGER.error(estr); + writeError(req.path.toString(), estr); + continue; + } + + } + /* + * Modify prefix attributes + * + */ + else if (req.op == Operation.SET && req.t == Type.Request){ + + String reqPath = "/" + Ncs._services_ + "/" + nipap.prefix + ":" + nipap.prefix + "/" + + nipap._from_pool_ + req.pool_key + "/" + nipap._request_ + req.request_key; + + try { + updatePrefixInNIPAP(reqPath); + } catch (JnipapException e) { + String estr = "Update of prefix failed: " + e.getMessage(); + LOGGER.error(estr); + writeError(req.path.toString(), estr); + continue; + } - // Tell the subscription we are done + } + /* + * Modify from-prefix attributes + * + */ + else if (req.op == Operation.SET && req.t == Type.FromPrefixRequest){ + + String reqPath = "/" + Ncs._services_ + "/" + nipap.prefix + ":" + nipap.prefix + "/" + + nipap._from_pool_ + req.pool_key + "/" + nipap._request_ + req.request_key + "/" + + nipap._from_prefix_request_ + req.prefix_key; + + try { + updatePrefixInNIPAP(reqPath); + } catch (Exception e) { + String estr = "Update of from-prefix failed: " + e.getMessage(); + LOGGER.error(estr); + writeError(req.path.toString(), estr); + continue; + } + } + } catch (ConfException|IOException e) { + LOGGER.error("Request failed: " + e.getMessage()); + } + } sub.sync(CdbSubscriptionSyncType.DONE_PRIORITY); } } @@ -235,19 +590,21 @@ public void finish() { private void safeclose(Cdb s) { try {s.close();} - catch (Exception ignore) {} + catch (Exception ignore) { + } } - private enum Operation { ALLOCATE, DEALLOCATE } - private enum Type { Prefix } + private enum Operation { ALLOCATE, DEALLOCATE, SET } + private enum Type { Request, FromPrefixRequest, Prefix } private class Request { Operation op; Type t; ConfPath path; - ConfKey pool_key; + ConfKey pool_key; ConfKey request_key; + ConfKey prefix_key; } private class Iter implements CdbDiffIterate { @@ -261,7 +618,7 @@ public DiffIterateResultFlag iterate( ConfObject[] kp, DiffIterateOperFlag op, ConfObject oldValue, - ConfObject newValue, Object initstate) { + ConfObject newValue, Object initstate) { @SuppressWarnings("unchecked") ArrayList reqs = (ArrayList) initstate; @@ -269,61 +626,140 @@ public DiffIterateResultFlag iterate( try { ConfPath p = new ConfPath(kp); LOGGER.info("ITER " + op + " " + p); - // The kp array contains the keypath to the ConfObject in reverse order, for example: - // /ncs:services/nipap:nipap/prefix{bar} -> ["{bar}", "nipap:prefix", "nipap:nipap", "ncs:services" ] - // Since we are subscribing to the changes on /ncs:services/ura:ura, the 3rd node from the end of the list always contains the service name (list key) + LOGGER.info("length " + kp.length); Request r = new Request(); r.path = p; - if (kp[1].toString().equals("nipap:request")) { - r.request_key = (ConfKey)kp[0]; - if (kp[3].toString().equals("nipap:pool")) { - r.t = Type.Prefix; - r.pool_key = (ConfKey)kp[2]; - } - if (op == DiffIterateOperFlag.MOP_CREATED) { - r.op = Operation.ALLOCATE; - reqs.add(r); - } else if (op == DiffIterateOperFlag.MOP_DELETED) { - r.op = Operation.DEALLOCATE; - reqs.add(r); - } - } + + switch(op) { + + case MOP_CREATED: { + // new request + if (kp[1].toString().equals("nipap:request") && + kp.length == 6){ + r.pool_key = (ConfKey)kp[2]; + r.request_key = (ConfKey)kp[0]; + r.t = Type.Request; + r.op = Operation.ALLOCATE; + reqs.add(r); + //the request is new, we dont need to look at children + return DiffIterateResultFlag.ITER_CONTINUE; + } + else if (kp[1].toString().equals("nipap:from-prefix-request") && + kp.length == 8){ + r.prefix_key = (ConfKey)kp[0]; + r.request_key = (ConfKey)kp[2]; + r.pool_key = (ConfKey)kp[4]; + r.t = Type.FromPrefixRequest; + r.op = Operation.ALLOCATE; + reqs.add(r); + //the request is new, we dont need to look at children + return DiffIterateResultFlag.ITER_CONTINUE; + } + break; + } + case MOP_DELETED: { + if (kp[1].toString().equals("nipap:request") && + kp.length == 6){ + r.pool_key = (ConfKey)kp[2]; + r.request_key = (ConfKey)kp[0]; + r.t = Type.Request; + r.op = Operation.DEALLOCATE; + reqs.add(r); + } + else if (kp[1].toString().equals("nipap:from-prefix-request") && + kp.length == 8){ + r.prefix_key = (ConfKey)kp[0]; + r.request_key = (ConfKey)kp[2]; + r.pool_key = (ConfKey)kp[4]; + r.t = Type.FromPrefixRequest; + r.op = Operation.DEALLOCATE; + reqs.add(r); + } + //we dont need to look at children + return DiffIterateResultFlag.ITER_CONTINUE; + } + case MOP_VALUE_SET: { + if (kp[1].toString().equals("nipap:attributes") && + kp.length == 8) { + r.pool_key = (ConfKey)kp[4]; + r.request_key = (ConfKey)kp[2]; + r.t = Type.Request; + r.op = Operation.SET; + + boolean found = false; + + for (Request req : reqs) { + + if (req.t.equals(Type.Request) && + req.request_key.equals(r.request_key)) { + found = true; + } + } + if (found == false) { + reqs.add(r); + } + + } else if (kp[3].toString().equals("nipap:from-prefix-request") && + kp.length == 10) { + + r.pool_key = (ConfKey)kp[6]; + r.request_key = (ConfKey)kp[4]; + r.prefix_key = (ConfKey)kp[2]; + r.t = Type.FromPrefixRequest; + r.op = Operation.SET; + + boolean found = false; + + for (Request req : reqs) { + if (req.t.equals(Type.FromPrefixRequest) && + req.request_key.equals(r.request_key) && + req.prefix_key.equals(r.prefix_key)) { + found = true; + } + } + if (found == false) { + LOGGER.info("add " + r.prefix_key); + reqs.add(r); + } + } + break; + } + } } catch (Exception e) { LOGGER.error("", e); } return DiffIterateResultFlag.ITER_RECURSE; - - } + } } // redeploy MUST be done in another thread, if not system // hangs, since the CDB subscriber cannot do its work - private void redeploy(String path) { + private void redeploy(ConfPath path) { Redeployer r = new Redeployer(path); Thread t = new Thread(r); t.start(); } private class Redeployer implements Runnable { - private String path; + private ConfPath path; private ConfKey k; private Maapi m; private Socket s; - public Redeployer(String path) { + public Redeployer(ConfPath path) { this.path = path; this.k = k; try { s = new Socket(NcsMain.getInstance().getNcsHost(), - NcsMain.getInstance().getNcsPort()); + NcsMain.getInstance().getNcsPort()); m = new Maapi(s); m.startUserSession("admin", - m.getSocket().getInetAddress(), - "system", - new String[] {"admin"}, - MaapiUserSessionFlag.PROTO_TCP); + m.getSocket().getInetAddress(), + "system", + new String[] {"admin"}, + MaapiUserSessionFlag.PROTO_TCP); } catch (Exception e) { System.err.println("redeployer exception: "+e); } @@ -339,17 +775,17 @@ public void run() { int counter = 0; while (true) { - Thread.sleep(50); - if (m.exists(tid, path)) - break; - if (counter++ == 40) { - break; - } - Thread.sleep(1000); + Thread.sleep(50); + if (m.exists(tid, path)) + break; + if (counter++ == 40) { + break; + } + Thread.sleep(1000); } m.requestAction(new ConfXMLParam[] {}, - path+"/reactive-re-deploy"); + path.toString()+"/reactive-re-deploy"); try { m.finishTrans(tid); } diff --git a/nso-nipap/nso-nipap/src/yang/nipap.yang b/nso-nipap/nso-nipap/src/yang/nipap.yang index 75d543367..bb61339a2 100644 --- a/nso-nipap/nso-nipap/src/yang/nipap.yang +++ b/nso-nipap/nso-nipap/src/yang/nipap.yang @@ -15,6 +15,75 @@ module nipap { prefix ncs; } + grouping prefix-attributes { + + leaf customer_id { + description "Customer identifier"; + type string; + } + + leaf description { + description "Description of prefix"; + type string; + } + + leaf node { + description "Node, e.g. FQDN"; + type string; + } + + leaf order_id { + description "Order identifier"; + type string; + } + } + + grouping response-grouping { + container response { + description "CDB Subscriber will write the response here"; + config false; + tailf:cdb-oper { + tailf:persistent true; + } + + leaf status { + config false; + tailf:cdb-oper { + tailf:persistent true; + } + type enumeration { + enum ok; + enum error; + } + } + + leaf status-message { + config false; + tailf:cdb-oper { + tailf:persistent true; + } + type string; + } + + leaf prefix { + type inet:ip-prefix; + config false; + tailf:cdb-oper { + tailf:persistent true; + } + } + + leaf prefix_id { + type uint32; + config false; + tailf:cdb-oper { + tailf:persistent true; + } + } + uses prefix-attributes; + } + } + augment /ncs:services { description "NIPAP settings"; @@ -59,7 +128,7 @@ module nipap { } } - list pool { + list from-pool { key name; leaf name { tailf:info "Name of pool to request prefix from"; @@ -70,86 +139,62 @@ module nipap { list request { key name; leaf name { - tailf:info "Name of pool to request prefix from"; + tailf:info "Request name"; tailf:cli-allow-range; type string; } - leaf description { - description "Description of prefix"; - type string; + container attributes { + description "Prefix attributes"; + uses prefix-attributes; } - leaf node { - description "Node, e.g. FQDN"; - type string; - } - - leaf family { - description "Address-family to request"; - default 4; - type enumeration { - enum 4 { - value 4; - } - enum 6 { - value 6; + container arguments { + description "Prefix arguments to the request"; + + leaf family { + description "Address family"; + default '4'; + type enumeration { + enum 4 { + value 4; + } + enum 6 { + value 6; + } } } - } - - leaf prefix_id { - type uint64; - config false; - tailf:cdb-oper { - tailf:persistent true; - } - } - leaf prefix { - type inet:ip-prefix; - config false; - tailf:cdb-oper { - tailf:persistent true; + leaf prefix-length { + description "Prefix length"; + type uint8; } } - leaf redeploy-service { - description "Redeploy service after succesful allocation"; - type string; - } - - } - - - } + list from-prefix-request { + description "Request prefix from the requested prefix"; + key name; + ordered-by user; + leaf name { + description "Uniqe identifier for the prefix"; + type string; + } - list prefix { - key name; - leaf name { - tailf:info "Name of request"; - tailf:cli-allow-range; - type string; - } + container attributes { + description "Prefix attributes"; + uses prefix-attributes; + } - leaf backend { - type leafref { - path "../../backend/name"; - } - } + uses response-grouping; + } - leaf from-pool { - type string; - } + uses response-grouping; - leaf prefix { - type inet:ip-prefix; - config false; - tailf:cdb-oper { - tailf:persistent true; - } + leaf redeploy-service { + description "Redeploy service after succesful allocation"; + type string; + } } - } } } diff --git a/nso-nipap/nso-nipap/templates/nso-nipap-from-prefix-request.xml b/nso-nipap/nso-nipap/templates/nso-nipap-from-prefix-request.xml new file mode 100644 index 000000000..70deeb3b2 --- /dev/null +++ b/nso-nipap/nso-nipap/templates/nso-nipap-from-prefix-request.xml @@ -0,0 +1,21 @@ + + + + + { $POOL_NAME } + + { $ALLOCATION_NAME } + + { $FROM_PREFIX_ALLOCATION_NAME } + + { $CUSTOMER_ID } + { $DESCRIPTION } + { $NODE } + { $ORDER_ID } + + + + + + + diff --git a/nso-nipap/nso-nipap/templates/nso-nipap-prefix-request.xml b/nso-nipap/nso-nipap/templates/nso-nipap-prefix-request.xml new file mode 100644 index 000000000..dc542b373 --- /dev/null +++ b/nso-nipap/nso-nipap/templates/nso-nipap-prefix-request.xml @@ -0,0 +1,23 @@ + + + + + { $POOL_NAME } + + { $ALLOCATION_NAME } + + { $CUSTOMER_ID } + { $DESCRIPTION } + { $NODE } + { $ORDER_ID } + + + { $FAMILY } + { $PREFIX_LENGTH } + + { $SERVICE } + + + + +