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 }
+
+
+
+
+