Add support for user-friendly names in YANG data with aliases in KV store

Change-Id: I8b4fbb290dfb467b401e8cf20b825ddbb3bf897e
diff --git a/internal/sysrepo/callbacks.go b/internal/sysrepo/callbacks.go
index 27c8362..b7c7570 100644
--- a/internal/sysrepo/callbacks.go
+++ b/internal/sysrepo/callbacks.go
@@ -265,13 +265,18 @@
 		cTag = strconv.Itoa(core.VolthaVlanIdAny)
 	}
 
+	alias := core.ServiceAlias{
+		Key: core.ServiceKey{
+			Port: portName,
+			CTag: cTag,
+			STag: sTag,
+			TpId: tpId,
+		},
+		ServiceName: serviceName,
+		VlansName:   vlanName,
+	}
 	logger.Infow(ctx, "new-service-profile-information", log.Fields{
-		"service":  serviceName,
-		"port":     portName,
-		"vlanName": vlanName,
-		"tpId":     tpId,
-		"sTag":     sTag,
-		"cTag":     cTag,
+		"serviceInfo": alias,
 	})
 
 	if core.AdapterInstance == nil {
@@ -287,6 +292,13 @@
 		return C.SR_ERR_OPERATION_FAILED
 	}
 
+	if err := core.AdapterInstance.StoreServiceAlias(ctx, alias); err != nil {
+		//Log the error but don't make the callback fail
+		//The service in ONOS has been provisioned succesfully and the datastore has to stay aligned
+		//A fallback alias will be created if service data is requested later
+		logger.Errorw(ctx, "cannot-store-service-alias-in-kvstore", log.Fields{"err": err, "service": serviceName})
+	}
+
 	logger.Infow(ctx, "service-profile-creation-request-served", log.Fields{
 		"service": serviceName,
 	})
@@ -335,13 +347,18 @@
 		cTag = strconv.Itoa(core.VolthaVlanIdAny)
 	}
 
+	alias := core.ServiceAlias{
+		Key: core.ServiceKey{
+			Port: portName,
+			CTag: cTag,
+			STag: sTag,
+			TpId: tpId,
+		},
+		ServiceName: serviceName,
+		VlansName:   vlanName,
+	}
 	logger.Infow(ctx, "service-profile-deletion-information", log.Fields{
-		"service":  serviceName,
-		"port":     portName,
-		"vlanName": vlanName,
-		"tpId":     tpId,
-		"sTag":     sTag,
-		"cTag":     cTag,
+		"serviceInfo": alias,
 	})
 
 	if err := core.AdapterInstance.RemoveService(portName, sTag, cTag, tpId); err != nil {
@@ -352,6 +369,13 @@
 		return C.SR_ERR_OPERATION_FAILED
 	}
 
+	if err := core.AdapterInstance.DeleteServiceAlias(ctx, alias.Key); err != nil {
+		//Log the error but don't make the callback fail
+		//The service in ONOS has been removed succesfully and the datastore has to stay aligned
+		//The only side effect is a dangling alias left in the KV store
+		logger.Errorw(ctx, "cannot-delete-service-alias-from-kvstore", log.Fields{"err": err, "service": serviceName})
+	}
+
 	logger.Infow(ctx, "service-profile-removal-request-served", log.Fields{
 		"service": serviceName,
 	})
diff --git a/internal/sysrepo/sysrepo.go b/internal/sysrepo/sysrepo.go
index a2b19af..2297b66 100644
--- a/internal/sysrepo/sysrepo.go
+++ b/internal/sysrepo/sysrepo.go
@@ -125,6 +125,13 @@
 	//Bind the callback needed to support schema-mount
 	C.sr_set_ext_data_cb(plugin.connection, C.function(C.mountpoint_ext_data_clb), unsafe.Pointer(plugin.schemaMountData))
 
+	//Reconcile the content of the running datastore with the services in ONOS
+	//i.e. create yang nodes for already provisioned services
+	//so that they can be removed through NETCONF with an edit-config
+	if err := plugin.reconcileServices(ctx); err != nil {
+		return nil, fmt.Errorf("plugin-startup-cannot-reconcile-services: %v", err)
+	}
+
 	//Set callbacks for events
 
 	//Subscribe with a callback to the request of data on a certain path
@@ -305,3 +312,54 @@
 
 	return nil
 }
+
+//reconcileServices retrieves the currently active services from ONOS
+//and creates YANG nodes in the running datastore to allow their management
+//through NETCONF
+func (p *SysrepoPlugin) reconcileServices(ctx context.Context) error {
+	if core.AdapterInstance == nil {
+		return fmt.Errorf("nil-adapter-instance")
+	}
+
+	//Get and create VLANs first, otherwise validation for services will fail
+	vlansItems, err := core.AdapterInstance.GetVlans(ctx)
+	if err != nil {
+		return fmt.Errorf("cannot-get-programmed-vlans: %v", err)
+	}
+
+	if len(vlansItems) > 0 {
+		vlansTree, err := createYangTree(ctx, p.runningSession, vlansItems)
+		if err != nil {
+			return fmt.Errorf("cannot-create-vlans-tree: %v", err)
+		}
+		defer C.lyd_free_all(vlansTree)
+
+		err = editDatastore(ctx, p.runningSession, vlansTree)
+		if err != nil {
+			return fmt.Errorf("cannot-add-vlans-to-datastore: %v", err)
+		}
+	}
+
+	//Get and create nodes for services
+	servicesItems, err := core.AdapterInstance.GetServices(ctx)
+	if err != nil {
+		return fmt.Errorf("cannot-get-programmed-services: %v", err)
+	}
+
+	if len(servicesItems) > 0 {
+		servicesTree, err := createYangTree(ctx, p.runningSession, servicesItems)
+		if err != nil {
+			return fmt.Errorf("cannot-create-services-tree: %v", err)
+		}
+		defer C.lyd_free_all(servicesTree)
+
+		err = editDatastore(ctx, p.runningSession, servicesTree)
+		if err != nil {
+			return fmt.Errorf("cannot-add-services-to-datastore: %v", err)
+		}
+	}
+
+	logger.Info(ctx, "reconciled-active-services")
+
+	return nil
+}