Update CLI with complete get implementation 56/111856/3
authorRitu Sood <ritu.sood@intel.com>
Sat, 29 Aug 2020 08:27:53 +0000 (01:27 -0700)
committerRitu Sood <ritu.sood@intel.com>
Wed, 2 Sep 2020 20:59:58 +0000 (13:59 -0700)
CLI updated with get functionality. Also adding
vFw test case

Issue-ID: MULTICLOUD-1065
Signed-off-by: Ritu Sood <ritu.sood@intel.com>
Change-Id: I7bd22aca9fac9cb7b1f4c93d0ffad5b07b62cced

src/tools/emcoctl/Readme.md
src/tools/emcoctl/cmd/apply.go
src/tools/emcoctl/cmd/config.go
src/tools/emcoctl/cmd/delete.go
src/tools/emcoctl/cmd/get.go
src/tools/emcoctl/cmd/getall.go [deleted file]
src/tools/emcoctl/cmd/root.go
src/tools/emcoctl/cmd/utils.go
src/tools/emcoctl/examples/emco-cfg.yaml
src/tools/emcoctl/examples/vfw.yaml [new file with mode: 0644]

index bf07e56..ecbcf4d 100644 (file)
@@ -54,20 +54,33 @@ This command will apply the resources in the file. The user is responsible to en
 
 `$ emcoctl apply -f filename.yaml`
 
+For applying resources that don't have a json body anchor can be provided as an arguement
+
+`$ emcoctl apply <anchor>`
+
+`$ emcoctl apply projects/testvfw/composite-apps/compositevfw/v1/deployment-intent-groups/vfw_deployment_intent_group/instantiate`
+
+
 2. Get Emco Resources
 
-Get the resources in the input file. This command will use the metadata name to get the resource.
+Get the resources in the input file. This command will use the metadata name in each of the resources in the file to get information about the resource.
 
 `$ emcoctl get -f filename.yaml`
 
+For getting information for one resource anchor can be provided as an arguement
+
+`$ emcoctl get <anchor>`
+
+`$ emcoctl get projects/testvfw/composite-apps/compositevfw/v1/deployment-intent-groups/vfw_deployment_intent_group`
+
 3. Delete Emco Resources
 
-Delete resources in the file. The emcoctl will start deleting resources in the reverse order than given in the file to maintain hierarchy. This command will use the metadata name to delete the resource.
+Delete resources in the file. The emcoctl will start deleting resources in the reverse order than given in the file to maintain hierarchy. This command will use the metadata name in each of the resources in the file to delete the resource..
 
 `$ emcoctl delete -f filename.yaml`
 
-4. Get all Emco Resources
+For deleting one resource anchor can be provided as an arguement
 
-Get all for the resources in the file.
+`$ emcoctl delete <anchor>`
 
-`$ emcoctl getall -f filename.yaml`
+`$ emcoctl delete projects/testvfw/composite-apps/compositevfw/v1/deployment-intent-groups/vfw_deployment_intent_group`
index f451a61..cf494bc 100644 (file)
@@ -24,9 +24,8 @@ import (
 // applyCmd represents the apply command
 var applyCmd = &cobra.Command{
        Use:   "apply",
-       Short: "apply(Post) the resources from input file or url(with body) from command line",
+       Short: "apply(Post) the resources from input file or url(without body) from command line",
        Run: func(cmd *cobra.Command, args []string) {
-               fmt.Println("apply called")
                c := NewRestClient()
                if len(inputFiles) > 0 {
                        resources := readResources()
@@ -46,7 +45,6 @@ var applyCmd = &cobra.Command{
                                }
                        }
                } else if len(args) >= 1 {
-                       fmt.Println(args[0])
                        c.RestClientPost(args[0], []byte{})
                } else {
                        fmt.Println("Error: No args ")
@@ -55,8 +53,7 @@ var applyCmd = &cobra.Command{
 }
 
 func init() {
-       fmt.Println("INIT ")
        rootCmd.AddCommand(applyCmd)
        applyCmd.Flags().StringSliceVarP(&inputFiles, "filename", "f", []string{}, "Filename of the input file")
-       applyCmd.Flags().StringSliceVarP(&valuesFiles, "values", "v", []string{}, "Values to go with the file")
+       //applyCmd.Flags().StringSliceVarP(&valuesFiles, "values", "v", []string{}, "Values to go with the file")
 }
index c5e4466..8af1cc2 100644 (file)
@@ -39,6 +39,20 @@ const urlVersion string = "v2"
 const urlPrefix string = "http://"
 var Configurations EmcoConfigurations
 
+// SetDefaultConfiguration default configuration if t
+func SetDefaultConfiguration() {
+       Configurations.Orchestrator.Host = "localhost"
+       Configurations.Orchestrator.Port = 9015
+       Configurations.Clm.Host = "localhost"
+       Configurations.Clm.Port = 9061
+       Configurations.Ncm.Host = "localhost"
+       Configurations.Ncm.Port = 9031
+       Configurations.Dcm.Host = "localhost"
+       Configurations.Dcm.Port = 0
+       Configurations.OvnAction.Host = "localhost"
+       Configurations.OvnAction.Port = 9051
+}
+
 // GetOrchestratorURL Url for Orchestrator
 func GetOrchestratorURL() string {
        if Configurations.Orchestrator.Host == "" || Configurations.Orchestrator.Port == 0 {
index d6dbfe3..faa52b5 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright © 2020 Intel Corp 
+Copyright © 2020 Intel Corp
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -24,9 +24,8 @@ import (
 // deleteCmd represents the delete command
 var deleteCmd = &cobra.Command{
        Use:   "delete",
-       Short: "Delete resources in input file or commandline",
+       Short: "Delete the resources from input file or url from command line",
        Run: func(cmd *cobra.Command, args []string) {
-               fmt.Println("delete called")
                c := NewRestClient()
                if len(inputFiles) > 0 {
                        resources := readResources()
@@ -35,23 +34,14 @@ var deleteCmd = &cobra.Command{
                                c.RestClientDelete(res.anchor, res.body)
                        }
                } else if len(args) >= 1 {
-                       fmt.Println(args[0])
-                       c.RestClientDelete(args[0], nil)
+                       c.RestClientDeleteAnchor(args[0])
+               } else {
+                       fmt.Println("Error: No args ")
                }
        },
 }
 
 func init() {
        rootCmd.AddCommand(deleteCmd)
-
-       // Here you will define your flags and configuration settings.
-
-       // Cobra supports Persistent Flags which will work for this command
-       // and all subcommands, e.g.:
-       // deleteCmd.PersistentFlags().String("foo", "", "A help for foo")
-
-       // Cobra supports local flags which will only run when this command
-       // is called directly, e.g.:
-       // deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
        deleteCmd.Flags().StringSliceVarP(&inputFiles, "filename", "f", []string{}, "Filename of the input file")
 }
index 2cc96dc..124ceec 100644 (file)
@@ -24,17 +24,24 @@ import (
 // getCmd represents the get command
 var getCmd = &cobra.Command{
        Use:   "get",
-       Short: "Get the resource(s) based on the URL",
+       Short: "Get the resources from input file or url from command line",
        Run: func(cmd *cobra.Command, args []string) {
-               fmt.Println("get called")
                c := NewRestClient()
-               if len(args) >= 1 {
-                       fmt.Println(args[0])
-                       c.RestClientGet(args[0])
+               if len(inputFiles) > 0 {
+                       resources := readResources()
+                       c := NewRestClient()
+                       for _, res := range resources {
+                               c.RestClientGet(res.anchor, res.body)
+                       }
+               } else if len(args) >= 1 {
+                               c.RestClientGetAnchor(args[0])
+               } else {
+                       fmt.Println("Error: No args ")
                }
        },
 }
 
 func init() {
        rootCmd.AddCommand(getCmd)
+       getCmd.Flags().StringSliceVarP(&inputFiles, "filename", "f", []string{}, "Filename of the input file")
 }
diff --git a/src/tools/emcoctl/cmd/getall.go b/src/tools/emcoctl/cmd/getall.go
deleted file mode 100644 (file)
index 329b258..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-Copyright © 2020 Intel Corp
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-package cmd
-
-import (
-       "github.com/spf13/cobra"
-)
-
-// getallCmd represents the getall command
-var getallCmd = &cobra.Command{
-       Use:   "getall",
-       Short: "Get all resources in the file provided",
-       Run: func(cmd *cobra.Command, args []string) {
-               resources := readResources()
-               c := NewRestClient()
-               for _, res := range resources {
-                       c.RestClientGetAll(res.anchor)
-               }
-       },
-}
-
-func init() {
-       rootCmd.AddCommand(getallCmd)
-       // Here you will define your flags and configuration settings.
-       getallCmd.Flags().StringSliceVarP(&inputFiles, "filename", "f", []string{}, "Filename of the input file")
-}
index 4c1ac19..7648606 100644 (file)
@@ -38,7 +38,6 @@ var rootCmd = &cobra.Command{
 // This is called by main.main(). It only needs to happen once to the rootCmd.
 func Execute() {
        if err := rootCmd.Execute(); err != nil {
-               fmt.Println("Test")
                fmt.Println(err)
                os.Exit(1)
        }
@@ -55,7 +54,7 @@ func init() {
 
 // initConfig reads in config file and ENV variables if set.
 func initConfig() {
-       cfgFile = "emco-cfg.yaml"
+
        if cfgFile != "" {
                // Use config file from the flag.
                viper.SetConfigFile(cfgFile)
@@ -66,7 +65,6 @@ func initConfig() {
                        fmt.Println(err)
                        os.Exit(1)
                }
-               fmt.Println(home)
                // Search config in home directory with name ".emco" (without extension).
                viper.AddConfigPath(home)
                viper.SetConfigName(".emco")
@@ -81,5 +79,8 @@ func initConfig() {
                if err != nil {
                        fmt.Printf("Unable to decode into struct, %v", err)
                }
+       } else {
+               fmt.Println("Warning: No Configuration File found. Using defaults")
+               SetDefaultConfiguration()
        }
 }
\ No newline at end of file
index 34063ee..62b3375 100644 (file)
@@ -179,12 +179,30 @@ func (r RestyClient) RestClientMultipartPost(anchor string, body []byte, file st
        }
        return pkgerrors.Errorf("Server Multipart Post Error")
 }
-// RestClientGetAll returns all resource in the input file
-func (r RestyClient) RestClientGetAll(anchor string) error {
+
+// RestClientGetAnchor returns get data from anchor
+func (r RestyClient) RestClientGetAnchor(anchor string) error {
        url, err := GetURL(anchor)
        if err != nil {
                return err
        }
+       s := strings.Split(anchor, "/")
+       if len(s) >= 3 {
+               a := s[len(s)-2]
+               // Determine if multipart
+               if a == "apps" || a == "profiles" || a == "clusters" {
+                       // Supports only getting metadata
+                       resp, err := r.client.R().
+                               SetHeader("Accept", "application/json").
+                               Get(url)
+                       if err != nil {
+                               fmt.Println(err)
+                               return err
+                       }
+                       fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp)
+                       return nil
+               }
+       }
        resp, err := r.client.R().
                Get(url)
        if err != nil {
@@ -194,34 +212,58 @@ func (r RestyClient) RestClientGetAll(anchor string) error {
        fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp)
        return nil
 }
+
 // RestClientGet gets resource
-func (r RestyClient) RestClientGet(anchor string) error {
+func (r RestyClient) RestClientGet(anchor string, body []byte) error {
+       if anchor == "" {
+               return pkgerrors.Errorf("Anchor can't be empty")
+       }
        s := strings.Split(anchor, "/")
-       a := s[len(s)-2]
-       // Determine if multipart
-       if a == "apps" || a == "profiles" || a == "clusters" {
-               url, err := GetURL(anchor)
-               if err != nil {
-                       return err
-               }
-               // Supports only getting metadata
-               resp, err := r.client.R().
-                       SetHeader("Accept", "application/json").
-                       Get(url)
-               if err != nil {
-                       fmt.Println(err)
-                       return err
+       a := s[len(s)-1]
+       if a == "instantiate" || a == "apply" || a == "approve" || a == "terminate" {
+               // No get for these
+               return nil
+       }
+       var e emcoBody
+       err := json.Unmarshal(body, &e)
+       if err != nil {
+               fmt.Println(err)
+               return err
+       }
+       if e.Meta.Name != "" {
+               name := e.Meta.Name
+               anchor = anchor + "/" + name
+               if a == "composite-apps" {
+                       var cav emcoCompositeAppSpec
+                       err := mapstructure.Decode(e.Spec, &cav)
+                       if err != nil {
+                               fmt.Println("mapstruct error")
+                               return err
+                       }
+                       anchor = anchor + "/" + cav.Version
                }
-               fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp)
-       } else {
-               r.RestClientGetAll(anchor)
+       } else if e.Label != "" {
+               anchor = anchor + "/" + e.Label
        }
 
+       return r.RestClientGetAnchor(anchor)
+}
+// RestClientDeleteAnchor returns all resource in the input file
+func (r RestyClient) RestClientDeleteAnchor(anchor string) error {
+       url, err := GetURL(anchor)
+       if err != nil {
+               return err
+       }
+       resp, err := r.client.R().Delete(url)
+       if err != nil {
+               fmt.Println(err)
+               return err
+       }
+       fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode(), "Response:", resp)
        return nil
 }
 // RestClientDelete calls rest delete command
 func (r RestyClient) RestClientDelete(anchor string, body []byte) error {
-       var url string
 
        s := strings.Split(anchor, "/")
        a := s[len(s)-1]
@@ -229,13 +271,11 @@ func (r RestyClient) RestClientDelete(anchor string, body []byte) error {
                // Change instantiate to destroy
                s[len(s)-1] = "terminate"
                anchor = strings.Join(s[:], "/")
-               fmt.Println("URL:", anchor)
                return r.RestClientPost(anchor, []byte{})
        } else if a == "apply" {
                // Change apply to terminate
                s[len(s)-1] = "terminate"
                anchor = strings.Join(s[:], "/")
-               fmt.Println("URL:", anchor)
                return r.RestClientPost(anchor, []byte{})
        } else if a == "approve" || a == "status" {
                // Approve and status  doesn't have delete
@@ -261,19 +301,10 @@ func (r RestyClient) RestClientDelete(anchor string, body []byte) error {
                        }
                        anchor = anchor + "/" + cav.Version
                }
+       } else if e.Label != "" {
+               anchor = anchor + "/" + e.Label
        }
-       url, err = GetURL(anchor)
-       if err != nil {
-               return err
-       }
-       resp, err := r.client.R().
-               Delete(url)
-       if err != nil {
-               fmt.Println(err)
-               return err
-       }
-       fmt.Println("URL:", anchor, "Response Code:", resp.StatusCode())
-       return nil
+       return r.RestClientDeleteAnchor(anchor)
 }
 // GetURL reads the configuration file to get URL
 func GetURL(anchor string) (string, error) {
index f279065..c1703de 100644 (file)
@@ -3,4 +3,10 @@
     port: 9015
   clm:
     host: localhost
-    port: 9061
\ No newline at end of file
+    port: 9061
+  ncm:
+    host: localhost
+    port: 9031
+  ovnaction:
+    host: localhost
+    port: 9051
\ No newline at end of file
diff --git a/src/tools/emcoctl/examples/vfw.yaml b/src/tools/emcoctl/examples/vfw.yaml
new file mode 100644 (file)
index 0000000..b5df483
--- /dev/null
@@ -0,0 +1,408 @@
+#creating controller entries
+version: emco/v2
+resourceContext:
+  anchor: controllers
+metadata :
+   name: rsync
+spec:
+  host: "192.168.121.6"
+  port: 30546
+---
+
+#creating controller entries
+version: emco/v2
+resourceContext:
+  anchor: controllers
+metadata :
+   name: ovnaction
+spec:
+  host: "192.168.121.6"
+  port: 32259
+  type: "action"
+  priority: 1
+
+---
+
+#creating cluster provider
+version: emco/v2
+resourceContext:
+  anchor: cluster-providers
+metadata :
+   name: vfw-cluster-provider
+
+---
+#creating cluster
+version: emco/v2
+resourceContext:
+  anchor: cluster-providers/vfw-cluster-provider/clusters
+metadata :
+   name: edge01
+file:
+  kubeconfig
+
+---
+#Add label cluster
+version: emco/v2
+resourceContext:
+  anchor: cluster-providers/vfw-cluster-provider/clusters/edge01/labels
+label-name: LabelA
+
+---
+version: emco/v2
+resourceContext:
+  anchor: cluster-providers/vfw-cluster-provider/clusters/edge01/networks
+metadata:
+  name: emco-private-net
+spec:
+  cniType: ovn4nfv
+  ipv4Subnets:
+  - subnet: 10.10.20.0/24
+    name: subnet1
+    gateway: 10.10.20.1/24
+
+---
+version: emco/v2
+resourceContext:
+  anchor: cluster-providers/vfw-cluster-provider/clusters/edge01/networks
+metadata:
+  name: emco-unprotected-net
+spec:
+  cniType: ovn4nfv
+  ipv4Subnets:
+  - subnet: 192.168.10.0/24
+    name: subnet1
+    gateway: 192.168.10.1/24
+
+---
+version: emco/v2
+resourceContext:
+  anchor: cluster-providers/vfw-cluster-provider/clusters/edge01/networks
+metadata:
+  name: protected-private-net
+spec:
+  cniType: ovn4nfv
+  ipv4Subnets:
+  - subnet: 192.168.20.0/24
+    name: subnet1
+    gateway: 192.168.20.1/24
+
+---
+version: emco/v2
+resourceContext:
+  anchor: cluster-providers/vfw-cluster-provider/clusters/edge01/apply
+
+---
+#create project
+version: emco/v2
+resourceContext:
+  anchor: projects
+metadata :
+  name: testvfw
+
+---
+#creating collection composite app entry
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps
+metadata :
+  name: compositevfw
+spec:
+  version: v1
+
+---
+#adding prometheus app to the composite app
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/apps
+metadata :
+  name: packetgen
+file:
+  /home/vagrant/multicloud-k8s/kud/demo/composite-firewall/packetgen.tar.gz
+
+---
+#adding prometheus app to the composite app
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/apps
+metadata :
+  name: firewall
+file:
+  /home/vagrant/multicloud-k8s/kud/demo/composite-firewall/firewall.tar.gz
+
+---
+#adding collectd app to the composite app
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/apps
+metadata :
+  name: sink
+file:
+  /home/vagrant/multicloud-k8s/kud/demo/composite-firewall/sink.tar.gz
+
+---
+#creating collection composite profile entry
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/composite-profiles
+metadata :
+  name: vfw_composite-profile
+
+---
+#adding prometheus app profiles to the composite profile
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/composite-profiles/vfw_composite-profile/profiles
+metadata :
+  name: packetgen-profile
+spec:
+  app-name: packetgen
+file:
+  /home/vagrant/multicloud-k8s/kud/demo/composite-firewall/profile.tar.gz
+
+---
+#adding firewall app profiles to the composite profile
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/composite-profiles/vfw_composite-profile/profiles
+metadata :
+  name: firewall-profile
+spec:
+  app-name: firewall
+file:
+    /home/vagrant/multicloud-k8s/kud/demo/composite-firewall/profile.tar.gz
+
+---
+#adding firewall app profiles to the composite profile
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/composite-profiles/vfw_composite-profile/profiles
+metadata :
+  name: sink-profile
+spec:
+  app-name: sink
+file:
+    /home/vagrant/multicloud-k8s/kud/demo/composite-firewall/profile.tar.gz
+
+---
+#create the generic placement intent
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/generic-placement-intents
+metadata :
+  name: fw-placement-intent
+spec:
+  logical-cloud: NA
+
+---
+#add the prometheus app placement intent to the generic placement intent
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/generic-placement-intents/fw-placement-intent/app-intents
+metadata:
+  name: packetgen-placement-intent
+spec:
+  app-name: packetgen
+  intent:
+    allOf:
+    - provider-name: vfw-cluster-provider
+      cluster-label-name: LabelA
+---
+#add the prometheus app placement intent to the generic placement intent
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/generic-placement-intents/fw-placement-intent/app-intents
+metadata:
+  name: firewall-placement-intent
+spec:
+  app-name: firewall
+  intent:
+    allOf:
+    - provider-name: vfw-cluster-provider
+      cluster-label-name: LabelA
+
+---
+#add the prometheus app placement intent to the generic placement intent
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/generic-placement-intents/fw-placement-intent/app-intents
+metadata:
+  name: sink-placement-intent
+spec:
+  app-name: sink
+  intent:
+    allOf:
+    - provider-name: vfw-cluster-provider
+      cluster-label-name: LabelA
+
+---
+#creating cluster provider
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent
+metadata :
+   name: vfw_ovnaction_intent
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents
+metadata :
+   name: packetgen_workload_intent
+spec:
+  application-name: packetgen
+  workload-resource: r1-packetgen
+  type: Deployment
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents
+metadata :
+   name: firewall_workload_intent
+spec:
+  application-name: firewall
+  workload-resource: r1-firewall
+  type: Deployment
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents
+metadata :
+   name: sink_workload_intent
+spec:
+  application-name: sink
+  workload-resource: r1-sink
+  type: Deployment
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents/packetgen_workload_intent/interfaces
+metadata :
+   name: packetgen_unprotected_if
+spec:
+  interface: eth1
+  name: unprotected-private-net
+  defaultGateway: "false"
+  ipAddress: 192.168.10.2
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents/packetgen_workload_intent/interfaces
+metadata :
+   name: packetgen_emco_if
+spec:
+  interface: eth2
+  name: emco-private-net
+  defaultGateway: "false"
+  ipAddress: 10.10.20.2
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents/firewall_workload_intent/interfaces
+metadata :
+   name: firewall_emco_if
+spec:
+  interface: eth3
+  name: emco-private-net
+  defaultGateway: "false"
+  ipAddress: 10.10.20.3
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents/firewall_workload_intent/interfaces
+metadata :
+   name: firewall_unprotected_if
+spec:
+  interface: eth1
+  name: unprotected-private-net
+  defaultGateway: "false"
+  ipAddress: 192.168.10.3
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents/firewall_workload_intent/interfaces
+metadata :
+   name: firewall_protected_if
+spec:
+  interface: eth2
+  name: protected-private-net
+  defaultGateway: "false"
+  ipAddress: 192.168.20.2
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents/sink_workload_intent/interfaces
+metadata :
+   name: sink_protected_if
+spec:
+  interface: eth1
+  name: protected-private-net
+  defaultGateway: "false"
+  ipAddress: 192.168.20.3
+
+---
+#
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/network-controller-intent/vfw_ovnaction_intent/workload-intents/sink_workload_intent/interfaces
+metadata :
+   name: sink_emco_if
+spec:
+  interface: eth2
+  name: emco-private-net
+  defaultGateway: "false"
+  ipAddress: 10.10.20.4
+
+---
+#create deployment intent group
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/deployment-intent-groups
+metadata :
+  name: vfw_deployment_intent_group
+spec:
+  profile: vfw_composite-profile
+  version: r1
+  override-values:
+  - app-name: packetgen
+    values:
+      ".Values.service.ports.nodePort": '30888'
+  - app-name: firewall
+    values:
+      ".Values.global.dcaeCollectorIp": 1.2.3.4
+      ".Values.global.dcaeCollectorPort": '8888'
+  - app-name: sink
+    values:
+      ".Values.service.ports.nodePort": '30677'
+
+---
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/deployment-intent-groups/vfw_deployment_intent_group/intents
+metadata :
+  name: fw-deployment-intent
+spec:
+  intent:
+    genericPlacementIntent: fw-placement-intent
+    ovnaction: vfw_ovnaction_intent
+
+---
+version: emco/v2
+resourceContext:
+  anchor: projects/testvfw/composite-apps/compositevfw/v1/deployment-intent-groups/vfw_deployment_intent_group/approve