Add initial support for provisioning and removing services, getting service data
Change-Id: Ie49206d788a202e70a8d64f083c3f85b92ced8fb
diff --git a/internal/sysrepo/callbacks.go b/internal/sysrepo/callbacks.go
index 1256568..27c8362 100644
--- a/internal/sysrepo/callbacks.go
+++ b/internal/sysrepo/callbacks.go
@@ -21,6 +21,8 @@
import "C"
import (
"context"
+ "fmt"
+ "strconv"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
"github.com/opencord/voltha-northbound-bbf-adapter/internal/core"
@@ -65,3 +67,331 @@
return C.SR_ERR_OK
}
+
+//export get_services_cb
+func get_services_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
+ //This function is a callback for the retrieval of devices from sysrepo
+ //The "export" comment instructs CGO to create a C function for it
+
+ ctx := context.Background()
+ logger.Debug(ctx, "processing-get-services-request")
+
+ if session == nil {
+ logger.Error(ctx, "sysrepo-get-services-null-session")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ if parent == nil {
+ logger.Error(ctx, "sysrepo-get-services-null-parent-node")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ if core.AdapterInstance == nil {
+ logger.Error(ctx, "sysrepo-get-services-nil-translator")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ services, err := core.AdapterInstance.GetServices(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "sysrepo-get-services-translation-error", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ err = updateYangTree(ctx, session, parent, services)
+ if err != nil {
+ logger.Errorw(ctx, "sysrepo-get-services-update-error", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ logger.Info(ctx, "services-information-request-served")
+
+ return C.SR_ERR_OK
+}
+
+//export get_vlans_cb
+func get_vlans_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
+ //This function is a callback for the retrieval of vlans from sysrepo
+ //The "export" comment instructs CGO to create a C function for it
+
+ ctx := context.Background()
+ logger.Debug(ctx, "processing-get-vlans-request")
+
+ if session == nil {
+ logger.Error(ctx, "sysrepo-get-vlans-null-session")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ if parent == nil {
+ logger.Error(ctx, "sysrepo-get-vlans-null-parent-node")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ if core.AdapterInstance == nil {
+ logger.Error(ctx, "sysrepo-get-vlans-nil-translator")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ vlans, err := core.AdapterInstance.GetVlans(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "sysrepo-get-vlans-translation-error", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ err = updateYangTree(ctx, session, parent, vlans)
+ if err != nil {
+ logger.Errorw(ctx, "sysrepo-get-vlans-update-error", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ logger.Info(ctx, "vlans-information-request-served")
+
+ return C.SR_ERR_OK
+}
+
+//export get_bandwidth_profiles_cb
+func get_bandwidth_profiles_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
+ //This function is a callback for the retrieval of bandwidth profiles from sysrepo
+ //The "export" comment instructs CGO to create a C function for it
+
+ ctx := context.Background()
+ logger.Debug(ctx, "processing-get-bandwidth-profiles-request")
+
+ if session == nil {
+ logger.Error(ctx, "sysrepo-get-bandwidth-profiles-null-session")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ if parent == nil {
+ logger.Error(ctx, "sysrepo-get-bandwidth-profiles-null-parent-node")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ if core.AdapterInstance == nil {
+ logger.Error(ctx, "sysrepo-get-bandwidth-profiles-nil-translator")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ bwProfiles, err := core.AdapterInstance.GetBandwidthProfiles(ctx)
+ if err != nil {
+ logger.Errorw(ctx, "sysrepo-get-bandwidth-profiles-translation-error", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ err = updateYangTree(ctx, session, parent, bwProfiles)
+ if err != nil {
+ logger.Errorw(ctx, "sysrepo-get-bandwidth-profiles-update-error", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ logger.Info(ctx, "bandwidth-profiles-information-request-served")
+
+ return C.SR_ERR_OK
+}
+
+//export edit_service_profiles_cb
+func edit_service_profiles_cb(editSession *C.sr_session_ctx_t, runningSession *C.sr_session_ctx_t, event C.sr_event_t) C.sr_error_t {
+ //This function is a callback for changes on service profiles
+ //The "export" comment instructs CGO to create a C function for it
+
+ if event != C.SR_EV_CHANGE {
+ return C.SR_ERR_OK
+ }
+
+ ctx := context.Background()
+ logger.Debug(ctx, "processing-service-profile-changes")
+
+ serviceNamesChanges, err := getChangesList(ctx, editSession, core.ServiceProfilesPath+"/service-profile/name")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-names-changes", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ for _, n := range serviceNamesChanges {
+ switch n.Operation {
+ case C.SR_OP_CREATED:
+ if errCode := edit_service_create(ctx, editSession, runningSession, n.Value); errCode != C.SR_ERR_OK {
+ return errCode
+ }
+ case C.SR_OP_DELETED:
+ if errCode := edit_service_delete(ctx, editSession, runningSession, n.Value); errCode != C.SR_ERR_OK {
+ return errCode
+ }
+ default:
+ return C.SR_ERR_UNSUPPORTED
+ }
+ }
+
+ return C.SR_ERR_OK
+}
+
+func edit_service_create(ctx context.Context, editSession *C.sr_session_ctx_t, runningSession *C.sr_session_ctx_t, serviceName string) C.sr_error_t {
+ portName, err := getSingleChangeValue(ctx, editSession, fmt.Sprintf("%s/service-profile[name='%s']/ports/port/name", core.ServiceProfilesPath, serviceName))
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-port-changes", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ servicePortPath := core.GetServicePortPath(serviceName, portName)
+
+ tpId, err := getSingleChangeValue(ctx, editSession, servicePortPath+"/bbf-nt-service-profile-voltha:technology-profile-id")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-tp-id-change", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ vlanName, err := getSingleChangeValue(ctx, editSession, servicePortPath+"/port-vlans/port-vlan/name")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-vlan-change", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ vlansPath := core.GetVlansPath(vlanName)
+
+ sTag, err := getSingleChangeValue(ctx, editSession, vlansPath+"/ingress-rewrite/push-outer-tag/vlan-id")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-stag-changes", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+ if sTag == core.YangVlanIdAny {
+ sTag = strconv.Itoa(core.VolthaVlanIdAny)
+ }
+
+ cTag, err := getSingleChangeValue(ctx, editSession, vlansPath+"/ingress-rewrite/push-second-tag/vlan-id")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-stag-changes", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+ if cTag == core.YangVlanIdAny {
+ cTag = strconv.Itoa(core.VolthaVlanIdAny)
+ }
+
+ logger.Infow(ctx, "new-service-profile-information", log.Fields{
+ "service": serviceName,
+ "port": portName,
+ "vlanName": vlanName,
+ "tpId": tpId,
+ "sTag": sTag,
+ "cTag": cTag,
+ })
+
+ if core.AdapterInstance == nil {
+ logger.Error(ctx, "sysrepo-service-changes-nil-translator")
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ if err := core.AdapterInstance.ProvisionService(portName, sTag, cTag, tpId); err != nil {
+ logger.Errorw(ctx, "service-provisioning-error", log.Fields{
+ "service": serviceName,
+ "err": err,
+ })
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ logger.Infow(ctx, "service-profile-creation-request-served", log.Fields{
+ "service": serviceName,
+ })
+
+ return C.SR_ERR_OK
+}
+
+func edit_service_delete(ctx context.Context, editSession *C.sr_session_ctx_t, runningSession *C.sr_session_ctx_t, serviceName string) C.sr_error_t {
+ portName, err := getDatastoreLeafValue(ctx, runningSession, fmt.Sprintf("%s/service-profile[name='%s']/ports/port/name", core.ServiceProfilesPath, serviceName))
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-port-leaf", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ servicePortPath := core.GetServicePortPath(serviceName, portName)
+
+ tpId, err := getDatastoreLeafValue(ctx, runningSession, servicePortPath+"/bbf-nt-service-profile-voltha:technology-profile-id")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-tp-id-leaf", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ vlanName, err := getDatastoreLeafValue(ctx, runningSession, servicePortPath+"/port-vlans/port-vlan/name")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-vlan-leaf", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ vlansPath := core.GetVlansPath(vlanName)
+
+ sTag, err := getDatastoreLeafValue(ctx, runningSession, vlansPath+"/ingress-rewrite/push-outer-tag/vlan-id")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-stag-leaf", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+ if sTag == core.YangVlanIdAny {
+ sTag = strconv.Itoa(core.VolthaVlanIdAny)
+ }
+
+ cTag, err := getDatastoreLeafValue(ctx, runningSession, vlansPath+"/ingress-rewrite/push-second-tag/vlan-id")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-service-profile-stag-leaf", log.Fields{"err": err, "service": serviceName})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+ if cTag == core.YangVlanIdAny {
+ cTag = strconv.Itoa(core.VolthaVlanIdAny)
+ }
+
+ logger.Infow(ctx, "service-profile-deletion-information", log.Fields{
+ "service": serviceName,
+ "port": portName,
+ "vlanName": vlanName,
+ "tpId": tpId,
+ "sTag": sTag,
+ "cTag": cTag,
+ })
+
+ if err := core.AdapterInstance.RemoveService(portName, sTag, cTag, tpId); err != nil {
+ logger.Errorw(ctx, "service-removal-error", log.Fields{
+ "service": serviceName,
+ "err": err,
+ })
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ logger.Infow(ctx, "service-profile-removal-request-served", log.Fields{
+ "service": serviceName,
+ })
+
+ return C.SR_ERR_OK
+}
+
+//export edit_vlans_cb
+func edit_vlans_cb(editSession *C.sr_session_ctx_t, event C.sr_event_t) C.sr_error_t {
+ //This function is a callback for changes on VLANs
+ //The "export" comment instructs CGO to create a C function for it
+
+ if event != C.SR_EV_CHANGE {
+ return C.SR_ERR_OK
+ }
+
+ ctx := context.Background()
+ logger.Debug(ctx, "processing-vlans-changes")
+
+ vlanChanges, err := getChangesList(ctx, editSession, core.VlansPath+"//.")
+ if err != nil {
+ logger.Errorw(ctx, "cannot-get-vlans-changes", log.Fields{"err": err})
+ return C.SR_ERR_OPERATION_FAILED
+ }
+
+ for _, n := range vlanChanges {
+ //VLANs must be defined through creation (for service provisioning)
+ //or deletion (for service removal). Changes to the VLAN values
+ //are not supported, because VOLTHA does not support dynamic changes
+ //to the service.
+ switch n.Operation {
+ case C.SR_OP_CREATED:
+ case C.SR_OP_DELETED:
+ //Everything will be handled in the services callback
+ //Just approve the change here
+ return C.SR_ERR_OK
+ default:
+ return C.SR_ERR_UNSUPPORTED
+ }
+ }
+
+ return C.SR_ERR_OK
+}