Creation of Policy Type Instance 27/8127/5
authornaman.gupta <naman.gupta@samsung.com>
Tue, 26 Apr 2022 16:29:05 +0000 (21:59 +0530)
committernaman.gupta <naman.gupta@samsung.com>
Mon, 2 May 2022 10:01:30 +0000 (15:31 +0530)
Api for Creation of Policy Type Instance

Signed-off-by: naman.gupta <naman.gupta@samsung.com>
Change-Id: I649dd94059263244645e7a8236ad058c3f0af56f

a1-go/go.mod
a1-go/go.sum
a1-go/pkg/restful/restful.go
a1-go/pkg/resthooks/resthooks.go
a1-go/pkg/resthooks/resthooks_test.go
a1-go/pkg/resthooks/types.go

index 3205ceb..a189744 100644 (file)
@@ -27,7 +27,7 @@ replace gerrit.o-ran-sc.org/r/ric-plt/sdlgo => gerrit.o-ran-sc.org/r/ric-plt/sdl
 replace gerrit.o-ran-sc.org/r/com/golog => gerrit.o-ran-sc.org/r/com/golog.git v0.0.2
 
 require (
-       gerrit.o-ran-sc.org/r/com/golog v0.0.2
+       gerrit.o-ran-sc.org/r/com/golog v0.0.2
        gerrit.o-ran-sc.org/r/ric-plt/sdlgo v0.7.0
        github.com/go-openapi/errors v0.19.9
        github.com/go-openapi/loads v0.19.7
@@ -38,6 +38,7 @@ require (
        github.com/go-openapi/validate v0.19.15
        github.com/jessevdk/go-flags v1.5.0
        github.com/stretchr/testify v1.6.1
+       github.com/xeipuuv/gojsonschema v1.2.0
        golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
-       k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect
+       k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect
 )
index 39efc66..c3e3bc3 100644 (file)
@@ -236,6 +236,12 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
 github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
 github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
index 26a229f..b494dab 100644 (file)
@@ -68,10 +68,22 @@ func (r *Restful) setupHandler() *operations.A1API {
        })
 
        api.A1MediatorA1ControllerGetPolicyTypeHandler = a1_mediator.A1ControllerGetPolicyTypeHandlerFunc(func(params a1_mediator.A1ControllerGetPolicyTypeParams) middleware.Responder {
-               a1.Logger.Error("handler for get policy type from policytypeID")
+               a1.Logger.Debug("handler for get policy type from policytypeID")
                return a1_mediator.NewA1ControllerGetPolicyTypeOK().WithPayload(r.rh.GetPolicyType(models.PolicyTypeID(params.PolicyTypeID)))
        })
 
+       api.A1MediatorA1ControllerCreateOrReplacePolicyInstanceHandler = a1_mediator.A1ControllerCreateOrReplacePolicyInstanceHandlerFunc(func(params a1_mediator.A1ControllerCreateOrReplacePolicyInstanceParams) middleware.Responder {
+               a1.Logger.Debug("handler for create policy type instance ")
+               if err = r.rh.CreatePolicyInstance(models.PolicyTypeID(params.PolicyTypeID), models.PolicyInstanceID(params.PolicyInstanceID), params.Body); err == nil {
+                       return a1_mediator.NewA1ControllerCreateOrReplacePolicyInstanceAccepted()
+               }
+               if r.rh.IsValidJson(err) {
+                       return a1_mediator.NewA1ControllerCreateOrReplacePolicyInstanceBadRequest()
+               }
+               return a1_mediator.NewA1ControllerCreateOrReplacePolicyInstanceServiceUnavailable()
+
+       })
+
        return api
 
 }
index 54ca39d..43011a4 100644 (file)
@@ -30,22 +30,44 @@ import (
        "gerrit.o-ran-sc.org/r/ric-plt/a1/pkg/a1"
        "gerrit.o-ran-sc.org/r/ric-plt/a1/pkg/models"
        "gerrit.o-ran-sc.org/r/ric-plt/sdlgo"
+       "github.com/santhosh-tekuri/jsonschema/v5"
+       "gopkg.in/yaml.v2"
 )
 
 const (
-       a1PolicyPrefix = "a1.policy_type."
-       a1MediatorNs   = "A1m_ns"
+       a1PolicyPrefix   = "a1.policy_type."
+       a1MediatorNs     = "A1m_ns"
+       a1InstancePrefix = "a1.policy_instance."
 )
 
 var typeAlreadyError = errors.New("Policy Type already exists")
+var InstanceAlreadyError = errors.New("Policy Instance already exists")
 var typeMismatchError = errors.New("Policytype Mismatch")
+var invalidJsonSchema = errors.New("Invalid Json ")
+var policyInstanceNotFoundError = errors.New("Policy Instance Not Found")
+var policyTypeNotFoundError = errors.New("Policy Type Not Found")
+
+func (rh *Resthook) IsPolicyTypePresent(err error) bool {
+       return err == policyTypeNotFoundError
+}
+
+func (rh *Resthook) IsPolicyInstanceNotFound(err error) bool {
+       return err == policyInstanceNotFoundError
+}
 
 func (rh *Resthook) IsTypeAlready(err error) bool {
        return err == typeAlreadyError
 }
+func (rh *Resthook) IsInstanceAlready(err error) bool {
+       return err == InstanceAlreadyError
+}
 func (rh *Resthook) IsTypeMismatch(err error) bool {
        return err == typeMismatchError
 }
+
+func (rh *Resthook) IsValidJson(err error) bool {
+       return err == invalidJsonSchema
+}
 func NewResthook() *Resthook {
        return createResthook(sdlgo.NewSyncStorage())
 }
@@ -102,7 +124,7 @@ func (rh *Resthook) GetPolicyType(policyTypeId models.PolicyTypeID) *models.Poli
 
        if err != nil {
                a1.Logger.Error("error in retrieving policy type. err: %v", err)
-               return policytypeschema
+               return nil
        }
 
        if valmap[key] == nil {
@@ -119,10 +141,12 @@ func (rh *Resthook) GetPolicyType(policyTypeId models.PolicyTypeID) *models.Poli
        valkey := "`" + valStr + "`"
        valToUnmarshall, err := strconv.Unquote(valkey)
        if err != nil {
-               panic(err)
+               a1.Logger.Error("unquote error : %+v", err)
+               return nil
        }
 
-       a1.Logger.Debug("Policy type for %+v :  %+v", key, string(b))
+       a1.Logger.Debug("Policy type for %+v :  %+v", key, string(valToUnmarshall))
+
        errunm := json.Unmarshal([]byte(valToUnmarshall), &item)
 
        a1.Logger.Debug(" Unmarshalled json : %+v", (errunm))
@@ -155,3 +179,154 @@ func (rh *Resthook) CreatePolicyType(policyTypeId models.PolicyTypeID, httpreque
        }
        return nil
 }
+
+func toStringKeys(val interface{}) (interface{}, error) {
+       var err error
+       switch val := val.(type) {
+       case map[interface{}]interface{}:
+               m := make(map[string]interface{})
+               for k, v := range val {
+                       k, ok := k.(string)
+                       if !ok {
+                               return nil, errors.New("found non-string key")
+                       }
+                       m[k], err = toStringKeys(v)
+                       if err != nil {
+                               return nil, err
+                       }
+               }
+               return m, nil
+       case []interface{}:
+               var l = make([]interface{}, len(val))
+               for i, v := range val {
+                       l[i], err = toStringKeys(v)
+                       if err != nil {
+                               return nil, err
+                       }
+               }
+               return l, nil
+       default:
+               return val, nil
+       }
+}
+
+func validate(httpBodyString string, schemaString string) bool {
+       var m interface{}
+       err := yaml.Unmarshal([]byte(httpBodyString), &m)
+       if err != nil {
+               a1.Logger.Error("Unmarshal error : %+v", err)
+       }
+       m, err = toStringKeys(m)
+       if err != nil {
+               a1.Logger.Error("Conversion to string error : %+v", err)
+               return false
+       }
+       compiler := jsonschema.NewCompiler()
+       if err := compiler.AddResource("schema.json", strings.NewReader(schemaString)); err != nil {
+               a1.Logger.Error("string reader error : %+v", err)
+               return false
+       }
+       schema, err := compiler.Compile("schema.json")
+       if err != nil {
+               a1.Logger.Error("schema json compile error : %+v", err)
+               return false
+       }
+       if err := schema.Validate(m); err != nil {
+               a1.Logger.Error("schema validation error : %+v", err)
+               return false
+       }
+       a1.Logger.Debug("validation successfull")
+       return true
+}
+
+func (rh *Resthook) StorePolicyInstance(policyTypeId models.PolicyTypeID, policyInstanceID models.PolicyInstanceID, httpBody interface{}) (string, error) {
+       var keys [1]string
+       operation := "CREATE"
+       typekey := a1PolicyPrefix + strconv.FormatInt((int64(policyTypeId)), 10)
+       keys[0] = typekey
+
+       a1.Logger.Debug("key1 : %+v", typekey)
+
+       valmap, err := rh.db.Get(a1MediatorNs, keys[:])
+       if err != nil {
+               a1.Logger.Error("policy type error : %+v", err)
+       }
+       a1.Logger.Debug("policytype map : %+v", valmap)
+       if valmap[typekey] == nil {
+               a1.Logger.Error("policy type Not Present for policyid : %v", policyTypeId)
+               return operation, policyTypeNotFoundError
+       }
+       // TODO : rmr creation_timestamp := time.Now() // will be needed for rmr to notify the creation of instance
+
+       instancekey := a1InstancePrefix + strconv.FormatInt((int64(policyTypeId)), 10) + "." + string(policyInstanceID)
+       keys[0] = typekey
+       instanceMap, err := rh.db.Get(a1MediatorNs, keys[:])
+       if err != nil {
+               a1.Logger.Error("policy type error : %v", err)
+       }
+       a1.Logger.Debug("policyinstancetype map : %+v", instanceMap)
+
+       if instanceMap[instancekey] != nil {
+               operation = "UPDATE"
+               a1.Logger.Debug("UPDATE")
+               data, _ := json.Marshal(httpBody)
+               a1.Logger.Debug("Marshaled String : %+v", string(data))
+               a1.Logger.Debug("key   : %+v", instancekey)
+               success, err1 := rh.db.SetIf(a1MediatorNs, instancekey, instanceMap[instancekey], string(data))
+               if err1 != nil {
+                       a1.Logger.Error("error2 :%+v", err1)
+                       return operation, err1
+               }
+               if !success {
+                       a1.Logger.Debug("Policy instance %+v already exist", policyInstanceID)
+                       return operation, InstanceAlreadyError
+               }
+       } else {
+               data, _ := json.Marshal(httpBody)
+               a1.Logger.Debug("Marshaled String : %+v", string(data))
+               a1.Logger.Debug("key   : %+v", instancekey)
+
+               var instance_map []interface{}
+               instance_map = append(instance_map, instancekey, string(data))
+               a1.Logger.Debug("policyinstancetype map : %+v", instance_map[1])
+               a1.Logger.Debug("policyinstancetype to create : %+v", instance_map)
+
+               err1 := rh.db.Set(a1MediatorNs, instancekey, string(data))
+               if err1 != nil {
+                       a1.Logger.Error("error1 :%+v", err1)
+                       return operation, err1
+               }
+       }
+       a1.Logger.Debug("Policy Instance created ")
+       return operation, nil
+}
+
+func (rh *Resthook) CreatePolicyInstance(policyTypeId models.PolicyTypeID, policyInstanceID models.PolicyInstanceID, httpBody interface{}) error {
+       a1.Logger.Debug("CreatePolicyInstance function")
+       //  validate the PUT against the schema
+       var policyTypeSchema *models.PolicyTypeSchema
+       policyTypeSchema = rh.GetPolicyType(policyTypeId)
+       schemaStr, err := json.Marshal(policyTypeSchema.CreateSchema)
+       if err != nil {
+               a1.Logger.Error("Json Marshal error : %+v", err)
+               return err
+       }
+       a1.Logger.Debug("schema to validate %+v", string(schemaStr))
+       a1.Logger.Debug("httpbody to validate %+v", httpBody)
+       schemaString := fmt.Sprint(string(schemaStr))
+       httpBodyString := fmt.Sprint((httpBody))
+       isvalid := validate(httpBodyString, schemaString)
+       if isvalid {
+               operation, err := rh.StorePolicyInstance(policyTypeId, policyInstanceID, httpBody)
+               if err != nil {
+                       a1.Logger.Error("error :%+v", err)
+                       return err
+               }
+               a1.Logger.Debug("policy instance :%+v", operation)
+       } else {
+               a1.Logger.Error("%+v", invalidJsonSchema)
+               return invalidJsonSchema
+       }
+
+       return nil
+}
index 55e22c3..cef23ea 100644 (file)
@@ -21,6 +21,7 @@
 package resthooks
 
 import (
+       "encoding/json"
        "os"
        "strconv"
        "testing"
@@ -109,6 +110,30 @@ func TestCreatePolicyType(t *testing.T) {
        sdlInst.AssertExpectations(t)
 }
 
+func TestCreatePolicyTypeInstance(t *testing.T) {
+       var policyTypeId models.PolicyTypeID
+       policyTypeId = 20001
+       var policyInstanceID models.PolicyInstanceID
+       policyInstanceID = "123456"
+       httpBody := `{
+               "enforce":true,
+               "window_length":20,
+          "blocking_rate":20,
+               "trigger_threshold":10
+               }`
+       instancekey := a1PolicyPrefix + strconv.FormatInt(20001, 10) + "." + string(policyInstanceID)
+       data, _ := json.Marshal(httpBody)
+       a1.Logger.Debug("Marshaled String : %+v", string(data))
+       a1.Logger.Debug("key   : %+v", instancekey)
+
+       instancearr := []interface{}{instancekey, string(data)}
+       sdlInst.On("Set", "A1m_ns", instancearr).Return("CREATE", nil)
+       errresp := rh.CreatePolicyInstance(policyTypeId, policyInstanceID, httpBody)
+
+       assert.Nil(t, errresp)
+       sdlInst.AssertExpectations(t)
+}
+
 type SdlMock struct {
        mock.Mock
 }
@@ -118,27 +143,38 @@ func (s *SdlMock) GetAll(ns string) ([]string, error) {
        return args.Get(0).([]string), nil
 }
 
-func (s *SdlMock) SetIfNotExists(ns string, key string, data interface{}) (bool, error) {
-       a1.Logger.Debug("SetIfNotExists mock called")
-       args := s.MethodCalled("SetIfNotExists", ns, key, data)
-       return args.Bool(0), nil
-}
-
 func (s *SdlMock) Get(ns string, keys []string) (map[string]interface{}, error) {
        a1.Logger.Debug("Get Called ")
        args := s.MethodCalled("Get", ns, keys)
        a1.Logger.Debug("keys :%+v", args.Get(1))
-       var policyTypeSchema models.PolicyTypeSchema
-       name := "admission_control_policy_mine"
-       policyTypeSchema.Name = &name
        policytypeid := int64(20001)
-       policyTypeSchema.PolicyTypeID = &policytypeid
-       description := "various parameters to control admission of dual connection"
-       policyTypeSchema.Description = &description
-       policyTypeSchema.CreateSchema = `{"$schema": "http://json-schema.org/draft-07/schema#","type":"object","properties": {"enforce": {"type":"boolean","default":"true",},"window_length": {"type":        "integer","default":1,"minimum":1,"maximum":60,"description": "Sliding window length (in minutes)",},
-"blocking_rate": {"type":"number","default":10,"minimum":1,"maximum":100,"description": "% Connections to block",},"additionalProperties": false,},}`
+
+       policyTypeSchemaString := `{"name":"admission_control_policy_mine","description":"various parameters to control admission of dual connection","policy_type_id": 20001,"create_schema":{"$schema": "http://json-schema.org/draft-07/schema#","type":    "object","properties": {"enforce": {"type":    "boolean","default": "true"},"window_length": {"type":"integer","default":     1,"minimum":     1,"maximum":     60,"description": "Sliding window length (in minutes)"},"blocking_rate": {"type":        "number","default":     10,"minimum":     1,"maximum":     1001,"description": "% Connections to block"},"additionalProperties": false}}}`
+
+       a1.Logger.Error(" policyTypeSchemaString %+v", policyTypeSchemaString)
+       policyTypeSchema, _ := json.Marshal((policyTypeSchemaString))
+       // a1.Logger.Error(" policyTypeSchema error %+v",  err)
+       a1.Logger.Error(" policyTypeSchema %+v", string(policyTypeSchema))
+       var p models.PolicyTypeSchema
+       _ = json.Unmarshal([]byte(string(policyTypeSchemaString)), &p)
+       a1.Logger.Error("unmarshalled  policyTypeSchema %+v", p.CreateSchema)
        key := a1PolicyPrefix + strconv.FormatInt((policytypeid), 10)
-       mp := map[string]interface{}{key: policyTypeSchema}
-       a1.Logger.Debug("Get Called and mp return %+v ", mp)
+       a1.Logger.Error(" key for policy type %+v", key)
+       mp := map[string]interface{}{key: string(policyTypeSchema)}
+       a1.Logger.Error("Get Called and mp return %+v ", mp)
        return mp, nil
 }
+
+func (s *SdlMock) SetIfNotExists(ns string, key string, data interface{}) (bool, error) {
+       args := s.MethodCalled("SetIfNotExists", ns, key, data)
+       return args.Bool(0), args.Error(1)
+}
+
+func (s *SdlMock) Set(ns string, pairs ...interface{}) error {
+       args := s.MethodCalled("Set", ns, pairs)
+       return args.Error(1)
+}
+func (s *SdlMock) SetIf(ns string, key string, oldData, newData interface{}) (bool, error) {
+       args := s.MethodCalled("SetIfNotExists", ns, key, oldData, newData)
+       return args.Bool(0), args.Error(1)
+}
index e84e048..8302874 100644 (file)
@@ -28,4 +28,6 @@ type iSdl interface {
        GetAll(string) ([]string, error)
        SetIfNotExists(ns string, key string, data interface{}) (bool, error)
        Get(string, []string) (map[string]interface{}, error)
+       SetIf(ns string, key string, oldData, newData interface{}) (bool, error)
+       Set(ns string, pairs ...interface{}) error
 }