summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INFO.yaml8
-rwxr-xr-xclover/cloverctl/build.sh36
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/clear.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/clear_visibility.go41
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/create.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/create_idsrules.go56
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/create_system.go37
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/create_testplan.go57
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/delete.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/delete_system.go33
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/get.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/get_services.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/get_testresult.go56
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/get_visibility.go41
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/init.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/init_visibility.go41
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/root.go90
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/start.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/start_ids.go42
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/start_testplan.go59
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/start_visibility.go60
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/stop.go26
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/stop_ids.go42
-rw-r--r--clover/cloverctl/src/cloverctl/cmd/stop_visibility.go42
-rw-r--r--clover/cloverctl/src/cloverctl/main.go14
-rw-r--r--clover/cloverctl/src/cloverctl/yaml/idsrule_scan.yaml9
-rw-r--r--clover/cloverctl/src/cloverctl/yaml/idsrule_tcp.yaml10
-rw-r--r--clover/cloverctl/src/cloverctl/yaml/jmeter_testplan.yaml11
-rw-r--r--clover/cloverctl/src/cloverctl/yaml/visibility.yaml7
-rw-r--r--clover/cloverctl/src/cloverinject/inject.go141
-rw-r--r--clover/cloverctl/src/cloverkube/main.go400
-rw-r--r--clover/collector/__init__.py0
-rwxr-xr-xclover/collector/build.sh16
-rw-r--r--clover/collector/db/__init__.py0
-rw-r--r--clover/collector/db/cassops.py144
-rw-r--r--clover/collector/db/redisops.py59
-rw-r--r--clover/collector/docker/Dockerfile30
-rw-r--r--clover/collector/grpc/__init__.py0
-rwxr-xr-xclover/collector/grpc/build_proto.sh11
-rw-r--r--clover/collector/grpc/collector.proto45
-rw-r--r--clover/collector/grpc/collector_client.py105
-rw-r--r--clover/collector/grpc/collector_pb2.py300
-rw-r--r--clover/collector/grpc/collector_pb2_grpc.py97
-rw-r--r--clover/collector/grpc/collector_server.py98
-rw-r--r--clover/collector/process/__init__.py0
-rw-r--r--clover/collector/process/collect.py162
-rwxr-xr-xclover/collector/process/grpc_process.sh11
-rw-r--r--clover/collector/yaml/manifest.template43
-rw-r--r--clover/collector/yaml/render_yaml.py73
-rw-r--r--clover/logging/install/elasticsearch-statefulset-service.yaml129
-rw-r--r--clover/logging/install/fluentd-daemonset-elasticsearch-rbac.yaml2
-rw-r--r--clover/monitoring/monitoring.py5
-rw-r--r--clover/test/fraser_a_b_test.py2
-rwxr-xr-xclover/tools/jmeter/build_master.sh16
-rwxr-xr-xclover/tools/jmeter/build_slave.sh16
-rw-r--r--clover/tools/jmeter/jmeter-master/Dockerfile29
-rwxr-xr-xclover/tools/jmeter/jmeter-master/grpc/build_proto.sh11
-rw-r--r--clover/tools/jmeter/jmeter-master/grpc/jmeter.proto43
-rw-r--r--clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py291
-rw-r--r--clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py80
-rw-r--r--clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py118
-rwxr-xr-xclover/tools/jmeter/jmeter-master/process/grpc_process.sh11
-rw-r--r--clover/tools/jmeter/jmeter-master/tests/jmx.template135
-rw-r--r--clover/tools/jmeter/jmeter-slave/Dockerfile27
-rw-r--r--clover/tools/jmeter/rmi_keystore.jksbin0 -> 2190 bytes
-rw-r--r--clover/tools/jmeter/yaml/manifest.template45
-rw-r--r--clover/tools/jmeter/yaml/render_master.py67
-rw-r--r--clover/tools/jmeter/yaml/render_slave.py67
-rw-r--r--clover/tools/yaml/cassandra.yaml97
-rw-r--r--edge/sample/Network.pngbin0 -> 41699 bytes
-rw-r--r--edge/sample/README.md54
-rw-r--r--edge/sample/ansible.cfg475
-rw-r--r--edge/sample/clusterForm.yml15
-rw-r--r--edge/sample/clusterTear.yml5
-rw-r--r--edge/sample/hosts16
-rw-r--r--edge/sample/roles/clusterForm_common/tasks/main.yml127
-rw-r--r--edge/sample/roles/clusterForm_master/tasks/main.yml72
-rw-r--r--edge/sample/roles/clusterForm_slave(s)/tasks/main.yml12
-rw-r--r--edge/sample/roles/clusterTear_common/tasks/main.yml21
-rw-r--r--samples/scenarios/clearwater_ims/clt-docker/Dockerfile22
-rw-r--r--samples/scenarios/clearwater_ims/clt-docker/clearwater-live-test.tgzbin0 -> 1627952 bytes
-rwxr-xr-xsamples/scenarios/clearwater_ims/scripts/prov-numbers.sh1
-rwxr-xr-xsamples/scenarios/clearwater_ims/scripts/run-live-test.sh40
-rw-r--r--samples/scenarios/clearwater_ims/yaml/ellis-depl.yaml40
-rw-r--r--samples/scenarios/clearwater_ims/yaml/ellis-svc.yaml16
-rw-r--r--samples/scenarios/clearwater_ims/yaml/homer-depl.yaml38
-rw-r--r--samples/scenarios/clearwater_ims/yaml/homer-svc.yaml14
-rw-r--r--samples/scenarios/clearwater_ims/yaml/homestead-depl.yaml54
-rw-r--r--samples/scenarios/clearwater_ims/yaml/homestead-prov-depl.yaml42
-rw-r--r--samples/scenarios/clearwater_ims/yaml/homestead-prov-svc.yaml14
-rw-r--r--samples/scenarios/clearwater_ims/yaml/homestead-svc.yaml14
-rw-r--r--samples/scenarios/clearwater_ims/yaml/ralf-depl.yaml54
-rw-r--r--samples/scenarios/clearwater_ims/yaml/ralf-svc.yaml14
-rw-r--r--samples/scenarios/service_delivery_controller_opnfv.yaml1
-rwxr-xr-xxci-k8s-setup.sh40
95 files changed, 5119 insertions, 8 deletions
diff --git a/INFO.yaml b/INFO.yaml
index abe5ba5..12700e1 100644
--- a/INFO.yaml
+++ b/INFO.yaml
@@ -4,6 +4,10 @@ project_creation_date: ''
project_category: ''
lifecycle_state: 'Incubation'
project_lead: &opnfv_clover_ptl
+ name: 'Wenjing Chu'
+ email: 'chu.wenjing@gmail.com'
+ company: 'gmail.com'
+ id: 'wenjing'
primary_contact: *opnfv_clover_ptl
issue_tracking:
type: 'jira'
@@ -29,10 +33,6 @@ repositories:
- 'clover'
committers:
- <<: *opnfv_clover_ptl
- - name: 'Wenjing Chu'
- email: 'chu.wenjing@gmail.com'
- company: 'gmail.com'
- id: 'wenjing'
- name: 'Stephen Wong'
email: 'stephen.kf.wong@gmail.com'
company: 'gmail.com'
diff --git a/clover/cloverctl/build.sh b/clover/cloverctl/build.sh
new file mode 100755
index 0000000..2f7be14
--- /dev/null
+++ b/clover/cloverctl/build.sh
@@ -0,0 +1,36 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+GOVERSION=1.10.3
+OS=linux
+ARCH=amd64
+GOPATH=/home/ubuntu/go
+CLIENTGOVERSION=v8.0.0
+
+# Install go on Ubuntu 16.04
+
+wget https://dl.google.com/go/go$GOVERSION.$OS-$ARCH.tar.gz
+sudo tar -C /usr/local -xzf go$GOVERSION.$OS-$ARCH.tar.gz
+export PATH=$PATH:/usr/local/go/bin
+export PATH=$GOPATH/bin:$PATH
+
+# Get dependencies
+
+go get github.com/ghodss/yaml
+go get github.com/tools/godep
+go get -u github.com/spf13/cobra/cobra
+go get -u gopkg.in/resty.v1
+
+go get k8s.io/client-go/...
+cd $GOPATH/src/k8s.io/client-go
+git checkout $CLIENTGOVERSION
+godep restore ./...
+rm -rf vendor/
+
+# Build cloverctl
+
+go install cloverctl
diff --git a/clover/cloverctl/src/cloverctl/cmd/clear.go b/clover/cloverctl/src/cloverctl/cmd/clear.go
new file mode 100644
index 0000000..309df70
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/clear.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+)
+
+var clearCmd = &cobra.Command{
+ Use: "clear",
+ Short: "Truncate visibility tables",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("clear called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(clearCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/clear_visibility.go b/clover/cloverctl/src/cloverctl/cmd/clear_visibility.go
new file mode 100644
index 0000000..9468714
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/clear_visibility.go
@@ -0,0 +1,41 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+)
+
+
+var visibilityclearCmd = &cobra.Command{
+ Use: "visibility",
+ Short: "Clear visibility tables",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ clearCollector()
+ },
+}
+
+func init() {
+ clearCmd.AddCommand(visibilityclearCmd)
+}
+
+func clearCollector() {
+ url := controllerIP + "/collector/truncate"
+
+ resp, err := resty.R().
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+}
+
+
diff --git a/clover/cloverctl/src/cloverctl/cmd/create.go b/clover/cloverctl/src/cloverctl/cmd/create.go
new file mode 100644
index 0000000..3a09fa4
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/create.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+)
+
+var createCmd = &cobra.Command{
+ Use: "create",
+ Short: "Create resources including IDS rules, L7 testplans, etc.",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("create called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(createCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/create_idsrules.go b/clover/cloverctl/src/cloverctl/cmd/create_idsrules.go
new file mode 100644
index 0000000..bc0d8d5
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/create_idsrules.go
@@ -0,0 +1,56 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "io/ioutil"
+ "gopkg.in/resty.v1"
+ "github.com/ghodss/yaml"
+ "github.com/spf13/cobra"
+)
+
+
+var idsrulesCmd = &cobra.Command{
+ Use: "idsrules",
+ Short: "Create one or many IDS rules from yaml file",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ createIDSRules()
+ },
+}
+
+func init() {
+ createCmd.AddCommand(idsrulesCmd)
+ idsrulesCmd.Flags().StringVarP(&cloverFile, "file", "f", "", "Input yaml file to add IDS rules")
+ idsrulesCmd.MarkFlagRequired("file")
+
+}
+
+func createIDSRules() {
+ url := controllerIP + "/snort/addrule"
+ in, err := ioutil.ReadFile(cloverFile)
+ if err != nil {
+ fmt.Println("Please specify a valid rule definition yaml file")
+ return
+ }
+ out_json, err := yaml.YAMLToJSON(in)
+ if err != nil {
+ panic(err.Error())
+ }
+ resp, err := resty.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(out_json).
+ Post(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+ //fmt.Println(string(out_json))
+
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/create_system.go b/clover/cloverctl/src/cloverctl/cmd/create_system.go
new file mode 100644
index 0000000..68fa5af
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/create_system.go
@@ -0,0 +1,37 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ //"io/ioutil"
+ //"github.com/ghodss/yaml"
+ "github.com/spf13/cobra"
+ "cloverkube"
+)
+
+
+var systemCmd = &cobra.Command{
+ Use: "system",
+ Short: "Deploy clover-system in Kubernetes",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ createCloverSystem()
+ },
+}
+
+func init() {
+ createCmd.AddCommand(systemCmd)
+ //systemCmd.PersistentFlags().StringVarP(&cloverFile, "f", "f", "", "Input yaml file to create system")
+
+}
+
+func createCloverSystem() {
+ cloverkube.DeployCloverSystem("create", "clover-system")
+ fmt.Println("Deployed clover-system successfully")
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/create_testplan.go b/clover/cloverctl/src/cloverctl/cmd/create_testplan.go
new file mode 100644
index 0000000..686d5ba
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/create_testplan.go
@@ -0,0 +1,57 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "io/ioutil"
+ "github.com/ghodss/yaml"
+ "github.com/spf13/cobra"
+)
+
+
+var testplanCmd = &cobra.Command{
+ Use: "testplan",
+ Short: "Create L7 client emulation test plans from yaml file",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ createTestPlan()
+ //fmt.Printf("%v\n", cmd.Parent().CommandPath())
+ },
+}
+
+func init() {
+ createCmd.AddCommand(testplanCmd)
+ testplanCmd.Flags().StringVarP(&cloverFile, "file", "f", "", "Input yaml file with test plan params")
+ testplanCmd.MarkFlagRequired("file")
+}
+
+func createTestPlan() {
+ url := controllerIP + "/jmeter/gen"
+ in, err := ioutil.ReadFile(cloverFile)
+ if err != nil {
+ fmt.Println("Please specify a valid test plan yaml file")
+ return
+ }
+ out_json, err := yaml.YAMLToJSON(in)
+ if err != nil {
+ panic(err.Error())
+ }
+ resp, err := resty.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(out_json).
+ Post(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+ //fmt.Println(string(out_json))
+
+}
+
diff --git a/clover/cloverctl/src/cloverctl/cmd/delete.go b/clover/cloverctl/src/cloverctl/cmd/delete.go
new file mode 100644
index 0000000..742d769
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/delete.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+)
+
+var deleteCmd = &cobra.Command{
+ Use: "delete",
+ Short: "Delete resources including clover-system services",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("delete called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(deleteCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/delete_system.go b/clover/cloverctl/src/cloverctl/cmd/delete_system.go
new file mode 100644
index 0000000..bc3f22b
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/delete_system.go
@@ -0,0 +1,33 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+ "cloverkube"
+)
+
+
+var delsystemCmd = &cobra.Command{
+ Use: "system",
+ Short: "Delete clover-system in Kubernetes",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ delCloverSystem()
+ },
+}
+
+func init() {
+ deleteCmd.AddCommand(delsystemCmd)
+}
+
+func delCloverSystem() {
+ cloverkube.DeployCloverSystem("delete", "clover-system")
+ fmt.Println("Deleted clover-system successfully")
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/get.go b/clover/cloverctl/src/cloverctl/cmd/get.go
new file mode 100644
index 0000000..ae3d98e
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/get.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+)
+
+var getCmd = &cobra.Command{
+ Use: "get",
+ Short: "Get information about a resource",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("get called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(getCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/get_services.go b/clover/cloverctl/src/cloverctl/cmd/get_services.go
new file mode 100644
index 0000000..cfa56bd
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/get_services.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+ "cloverkube"
+)
+
+var servicesCmd = &cobra.Command{
+ Use: "services",
+ Short: "Get info on Kubernetes services",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ cloverkube.GetServices()
+ },
+}
+
+func init() {
+ getCmd.AddCommand(servicesCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/get_testresult.go b/clover/cloverctl/src/cloverctl/cmd/get_testresult.go
new file mode 100644
index 0000000..12d47c3
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/get_testresult.go
@@ -0,0 +1,56 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+)
+
+var JmeterResult string
+
+var testresultCmd = &cobra.Command{
+ Use: "testresult",
+ Short: "Get test results from L7 client emulation",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ getResult()
+ },
+}
+
+func init() {
+ getCmd.AddCommand(testresultCmd)
+ testresultCmd.Flags().StringVarP(&JmeterResult, "r", "r", "", "Result to retrieve - use 'log' or 'results'")
+ testresultCmd.MarkFlagRequired("r")
+
+}
+
+
+func getResult() {
+ switch JmeterResult {
+ case "results":
+ url := controllerIP + "/jmeter/results/results"
+ resp, err := resty.R().
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\nResponse Body: %v\n", resp)
+ case "log":
+ url := controllerIP + "/jmeter/results/log"
+ resp, err := resty.R().
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\nResponse Body: %v\n", resp)
+ default:
+ fmt.Println("Unrecoginized jmeter result type - use 'log' or 'results'")
+ }
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/get_visibility.go b/clover/cloverctl/src/cloverctl/cmd/get_visibility.go
new file mode 100644
index 0000000..d987412
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/get_visibility.go
@@ -0,0 +1,41 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+)
+
+
+var visibilitystatsCmd = &cobra.Command{
+ Use: "visibility",
+ Short: "Get toplevel visibility stats",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ statsCollector()
+ },
+}
+
+func init() {
+ getCmd.AddCommand(visibilitystatsCmd)
+ //visibilitystartCmd.PersistentFlags().StringVarP(&cloverFile, "f", "f", "", "Input yaml file with test plan params")
+}
+
+func statsCollector() {
+ url := controllerIP + "/collector/stats"
+
+ resp, err := resty.R().
+ SetHeader("Accept", "application/json").
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\nProxy Response Time: %v\n", resp)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/init.go b/clover/cloverctl/src/cloverctl/cmd/init.go
new file mode 100644
index 0000000..613b263
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/init.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+)
+
+var initCmd = &cobra.Command{
+ Use: "init",
+ Short: "Initialize visibility schemas",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("init called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(initCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/init_visibility.go b/clover/cloverctl/src/cloverctl/cmd/init_visibility.go
new file mode 100644
index 0000000..ac9ec5c
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/init_visibility.go
@@ -0,0 +1,41 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+)
+
+
+var visibilityinitCmd = &cobra.Command{
+ Use: "visibility",
+ Short: "Init visibility data schemas",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ initCollector()
+ },
+}
+
+func init() {
+ initCmd.AddCommand(visibilityinitCmd)
+}
+
+func initCollector() {
+ url := controllerIP + "/collector/init"
+
+ resp, err := resty.R().
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+}
+
+
diff --git a/clover/cloverctl/src/cloverctl/cmd/root.go b/clover/cloverctl/src/cloverctl/cmd/root.go
new file mode 100644
index 0000000..6878077
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/root.go
@@ -0,0 +1,90 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "os"
+
+ homedir "github.com/mitchellh/go-homedir"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+ "cloverkube"
+)
+
+var cfgFile string
+
+var controllerIP string
+var cloverFile string
+
+// rootCmd represents the base command when called without any subcommands
+var rootCmd = &cobra.Command{
+ Use: "cloverctl",
+ Short: "Command-Line Interface (CLI) for Clover",
+ Long: ``,
+ // Uncomment the following line if your bare application
+ // has an action associated with it:
+ //Run: func(cmd *cobra.Command, args []string) {
+ //},
+}
+
+// Execute adds all child commands to the root command and sets flags appropriately.
+// 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(err)
+ os.Exit(1)
+ }
+}
+
+func init() {
+ cobra.OnInitialize(initConfig)
+
+ // Here you will define your flags and configuration settings.
+ // Cobra supports persistent flags, which, if defined here,
+ // will be global for your application.
+ rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cloverctl.yaml)")
+
+ // Cobra also supports local flags, which will only run
+ // when this action is called directly.
+ rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+
+ cPort, cIP := cloverkube.GetServicesPortIP("clover-controller")
+ if cIP == "" {
+ controllerIP = "http://10.244.0.1:" + fmt.Sprint(cPort)
+ } else {
+ controllerIP = "http://" + cIP
+ }
+ fmt.Printf("\nclover-controller: %s %s\n", fmt.Sprint(cPort), cIP)
+}
+
+// initConfig reads in config file and ENV variables if set.
+func initConfig() {
+ if cfgFile != "" {
+ // Use config file from the flag.
+ viper.SetConfigFile(cfgFile)
+ } else {
+ // Find home directory.
+ home, err := homedir.Dir()
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+
+ // Search config in home directory with name ".cloverctl" (without extension).
+ viper.AddConfigPath(home)
+ viper.SetConfigName(".cloverctl")
+ }
+
+ viper.AutomaticEnv() // read in environment variables that match
+
+ // If a config file is found, read it in.
+ if err := viper.ReadInConfig(); err == nil {
+ fmt.Println("Using config file:", viper.ConfigFileUsed())
+ }
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/start.go b/clover/cloverctl/src/cloverctl/cmd/start.go
new file mode 100644
index 0000000..741eacd
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/start.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+)
+
+var startCmd = &cobra.Command{
+ Use: "start",
+ Short: "Start processes including tests, visibility and ingress services",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("start called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(startCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/start_ids.go b/clover/cloverctl/src/cloverctl/cmd/start_ids.go
new file mode 100644
index 0000000..0f495a7
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/start_ids.go
@@ -0,0 +1,42 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+)
+
+
+var startidsCmd = &cobra.Command{
+ Use: "ids",
+ Short: "Start IDS process",
+ Long: `Restart IDS process when adding custom rules`,
+ Run: func(cmd *cobra.Command, args []string) {
+ startIDS()
+ },
+}
+
+func init() {
+ startCmd.AddCommand(startidsCmd)
+}
+
+func startIDS() {
+
+ url := controllerIP + "/snort/start"
+
+ resp, err := resty.R().
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+}
+
+
diff --git a/clover/cloverctl/src/cloverctl/cmd/start_testplan.go b/clover/cloverctl/src/cloverctl/cmd/start_testplan.go
new file mode 100644
index 0000000..b516ad6
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/start_testplan.go
@@ -0,0 +1,59 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "strings"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+ "cloverkube"
+)
+
+
+var testplanstartCmd = &cobra.Command{
+ Use: "testplan",
+ Short: "Start a test for a given test plan",
+ Long: `Specify number of slaves to use with '-s' flag. Default is 0 slaves,
+which runs tests only from jmeter-master.`,
+ Run: func(cmd *cobra.Command, args []string) {
+ startTest()
+ //fmt.Printf("%v\n", cmd.Parent().CommandPath())
+ },
+}
+var num_slaves int
+
+func init() {
+ startCmd.AddCommand(testplanstartCmd)
+ testplanstartCmd.PersistentFlags().StringVarP(&cloverFile, "file", "f", "", "Currently unused")
+ testplanstartCmd.PersistentFlags().IntVarP(&num_slaves, "slaves", "s", 0, "Number of slaves to use")
+}
+
+func startTest() {
+
+ ips := cloverkube.GetPodsIP("clover-jmeter-slave", "default")
+ fmt.Printf("\njmeter-slaves found: %s\n", ips)
+ if num_slaves > len(ips) {
+ fmt.Printf("Number of slaves specified must be less than found: %d\n", len(ips))
+ return
+ }
+ ip_list := strings.Join(ips[0:num_slaves], ",")
+
+ url := controllerIP + "/jmeter/start"
+ resp, err := resty.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(fmt.Sprintf(`{"num_slaves":"%d", "slave_list":"%s"}`, num_slaves, ip_list)).
+ Post(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+
+}
+
+
diff --git a/clover/cloverctl/src/cloverctl/cmd/start_visibility.go b/clover/cloverctl/src/cloverctl/cmd/start_visibility.go
new file mode 100644
index 0000000..18f8aac
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/start_visibility.go
@@ -0,0 +1,60 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "io/ioutil"
+ "gopkg.in/resty.v1"
+ "github.com/ghodss/yaml"
+ "github.com/spf13/cobra"
+)
+
+
+var visibilitystartCmd = &cobra.Command{
+ Use: "visibility",
+ Short: "Start visibility data collection",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ startCollector()
+ },
+}
+
+func init() {
+ startCmd.AddCommand(visibilitystartCmd)
+ visibilitystartCmd.PersistentFlags().StringVarP(&cloverFile, "file", "f", "", "Optional yaml file with collector params")
+}
+
+func startCollector() {
+
+ var message_body string
+ if cloverFile != "" {
+ in, err := ioutil.ReadFile(cloverFile)
+ if err != nil {
+ panic(err.Error())
+ }
+ out_json, err := yaml.YAMLToJSON(in)
+ message_body = string(out_json)
+ if err != nil {
+ panic(err.Error())
+ }
+ } else {
+ message_body = `{"sample_interval":"10", "t_port":"80", "t_host":"jaeger-query.istio-system"}`
+ }
+ url := controllerIP + "/collector/start"
+ resp, err := resty.R().
+ SetHeader("Content-Type", "application/json").
+ SetBody(message_body).
+ Post(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+}
+
+
diff --git a/clover/cloverctl/src/cloverctl/cmd/stop.go b/clover/cloverctl/src/cloverctl/cmd/stop.go
new file mode 100644
index 0000000..cfb7245
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/stop.go
@@ -0,0 +1,26 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "github.com/spf13/cobra"
+)
+
+var stopCmd = &cobra.Command{
+ Use: "stop",
+ Short: "Stop processes including visibility and ingress services",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ fmt.Println("stop called")
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(stopCmd)
+}
diff --git a/clover/cloverctl/src/cloverctl/cmd/stop_ids.go b/clover/cloverctl/src/cloverctl/cmd/stop_ids.go
new file mode 100644
index 0000000..b39b1e9
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/stop_ids.go
@@ -0,0 +1,42 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+)
+
+
+var stopidsCmd = &cobra.Command{
+ Use: "ids",
+ Short: "Stop IDS process",
+ Long: `Restart IDS process when adding custom rules`,
+ Run: func(cmd *cobra.Command, args []string) {
+ stopIDS()
+ },
+}
+
+func init() {
+ stopCmd.AddCommand(stopidsCmd)
+}
+
+func stopIDS() {
+
+ url := controllerIP + "/snort/stop"
+
+ resp, err := resty.R().
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+}
+
+
diff --git a/clover/cloverctl/src/cloverctl/cmd/stop_visibility.go b/clover/cloverctl/src/cloverctl/cmd/stop_visibility.go
new file mode 100644
index 0000000..4233157
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/cmd/stop_visibility.go
@@ -0,0 +1,42 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cmd
+
+import (
+ "fmt"
+ "gopkg.in/resty.v1"
+ "github.com/spf13/cobra"
+)
+
+
+var visibilitystopCmd = &cobra.Command{
+ Use: "visibility",
+ Short: "Stop visibility data collection",
+ Long: ``,
+ Run: func(cmd *cobra.Command, args []string) {
+ stopCollector()
+ },
+}
+
+func init() {
+ stopCmd.AddCommand(visibilitystopCmd)
+}
+
+func stopCollector() {
+
+ url := controllerIP + "/collector/stop"
+
+ resp, err := resty.R().
+ Get(url)
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf("\n%v\n", resp)
+}
+
+
diff --git a/clover/cloverctl/src/cloverctl/main.go b/clover/cloverctl/src/cloverctl/main.go
new file mode 100644
index 0000000..602dd20
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/main.go
@@ -0,0 +1,14 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package main
+
+import "cloverctl/cmd"
+
+func main() {
+ cmd.Execute()
+}
diff --git a/clover/cloverctl/src/cloverctl/yaml/idsrule_scan.yaml b/clover/cloverctl/src/cloverctl/yaml/idsrule_scan.yaml
new file mode 100644
index 0000000..1cce7f7
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/yaml/idsrule_scan.yaml
@@ -0,0 +1,9 @@
+sid: "10000003"
+protocol: tcp
+dest_port: any
+dest_ip: $HOME_NET
+src_port: any
+src_ip: any
+msg: MALWARE-CNC User-Agent ASafaWeb Scan
+rev: "001"
+content: '"asafaweb.com"'
diff --git a/clover/cloverctl/src/cloverctl/yaml/idsrule_tcp.yaml b/clover/cloverctl/src/cloverctl/yaml/idsrule_tcp.yaml
new file mode 100644
index 0000000..8711f5d
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/yaml/idsrule_tcp.yaml
@@ -0,0 +1,10 @@
+sid: "10000002"
+protocol: tcp
+dest_port: any
+dest_ip: $HOME_NET
+src_port: any
+src_ip: any
+msg: test
+rev: "001"
+content: ''
+
diff --git a/clover/cloverctl/src/cloverctl/yaml/jmeter_testplan.yaml b/clover/cloverctl/src/cloverctl/yaml/jmeter_testplan.yaml
new file mode 100644
index 0000000..140e70f
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/yaml/jmeter_testplan.yaml
@@ -0,0 +1,11 @@
+load_spec:
+ num_threads: 1
+ loops: 1
+ ramp_time: 60
+url_list:
+ - name: url1
+ url: http://proxy-access-control.default:9180
+ method: GET
+ - name: url2
+ url: http://proxy-access-control.default:9180
+ method: GET
diff --git a/clover/cloverctl/src/cloverctl/yaml/visibility.yaml b/clover/cloverctl/src/cloverctl/yaml/visibility.yaml
new file mode 100644
index 0000000..20264d2
--- /dev/null
+++ b/clover/cloverctl/src/cloverctl/yaml/visibility.yaml
@@ -0,0 +1,7 @@
+sample_interval: "10"
+#t_host: jaeger-deployment.istio-system
+#t_port: "16686"
+t_host: jaeger-query.istio-system
+t_port: "80"
+m_port: "9090"
+m_host: prometheus.istio-system
diff --git a/clover/cloverctl/src/cloverinject/inject.go b/clover/cloverctl/src/cloverinject/inject.go
new file mode 100644
index 0000000..1953a3b
--- /dev/null
+++ b/clover/cloverctl/src/cloverinject/inject.go
@@ -0,0 +1,141 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cloverinject
+
+import (
+ "io"
+ "os"
+ "fmt"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "istio.io/istio/pilot/pkg/kube/inject"
+ meshconfig "istio.io/api/mesh/v1alpha1"
+ "k8s.io/client-go/kubernetes"
+ "istio.io/istio/pkg/kube"
+ "istio.io/istio/pilot/pkg/model"
+ "github.com/ghodss/yaml"
+ "istio.io/istio/pilot/cmd"
+
+)
+//var _ = inject.InitImageName
+
+func CloverInject(inFilename string) {
+
+ var err error
+ var kubeconfig string
+
+ var reader io.Reader
+
+
+ var in *os.File
+ in, err = os.Open(inFilename)
+ if err != nil {
+ panic(err.Error())
+ }
+ reader = in
+
+ var writer io.Writer
+ outFilename := "out_sdc.yaml"
+ var out *os.File
+ out, err = os.Create(outFilename)
+ writer = out
+
+ var sidecarTemplate string
+ if sidecarTemplate, err = getInjectConfigFromConfigMap(kubeconfig); err != nil {
+ fmt.Printf("this is a template gen error")
+ panic(err.Error())
+ }
+
+ var meshConfig *meshconfig.MeshConfig
+ meshConfigFile := "mesh-config.yaml"
+ if meshConfig, err = cmd.ReadMeshConfig(meshConfigFile); err != nil {
+ panic(err.Error())
+ }
+
+ inject.IntoResourceFile(sidecarTemplate, meshConfig, reader, writer)
+
+}
+
+func getMeshConfigFromConfigMap(kubeconfig string) (*meshconfig.MeshConfig, error) {
+ client, err := createInterface(kubeconfig)
+ if err != nil {
+ return nil, err
+ }
+
+ istioNamespace := "istio-system"
+ meshConfigMapName := "istio"
+ configMapKey := "mesh"
+
+ config, err := client.CoreV1().ConfigMaps(istioNamespace).Get(meshConfigMapName, metav1.GetOptions{})
+ if err != nil {
+ return nil, fmt.Errorf("could not read valid configmap %q from namespace %q: %v - "+
+ "Use --meshConfigFile or re-run kube-inject with `-i <istioSystemNamespace> and ensure valid MeshConfig exists",
+ meshConfigMapName, istioNamespace, err)
+ }
+ // values in the data are strings, while proto might use a
+ // different data type. therefore, we have to get a value by a
+ // key
+ configYaml, exists := config.Data[configMapKey]
+ if !exists {
+ return nil, fmt.Errorf("missing configuration map key %q", configMapKey)
+ }
+ return model.ApplyMeshConfigDefaults(configYaml)
+}
+
+func getInjectConfigFromConfigMap(kubeconfig string) (string, error) {
+ client, err := createInterface(kubeconfig)
+ if err != nil {
+ return "", err
+ }
+ // added by me
+ istioNamespace := "istio-system"
+ injectConfigMapName := "istio-inject"
+ //injectConfigMapName := "istio-sidecar-injector"
+ injectConfigMapKey := "config"
+
+
+ config, err := client.CoreV1().ConfigMaps(istioNamespace).Get(injectConfigMapName, metav1.GetOptions{})
+ if err != nil {
+ return "", fmt.Errorf("could not find valid configmap %q from namespace %q: %v - "+
+ "Use --injectConfigFile or re-run kube-inject with `-i <istioSystemNamespace> and ensure istio-inject configmap exists",
+ injectConfigMapName, istioNamespace, err)
+ }
+ // values in the data are strings, while proto might use a
+ // different data type. therefore, we have to get a value by a
+ // key
+ injectData, exists := config.Data[injectConfigMapKey]
+ if !exists {
+ return "", fmt.Errorf("missing configuration map key %q in %q",
+ injectConfigMapKey, injectConfigMapName)
+ }
+ var injectConfig inject.Config
+ if err := yaml.Unmarshal([]byte(injectData), &injectConfig); err != nil {
+ return "", fmt.Errorf("unable to convert data from configmap %q: %v",
+ injectConfigMapName, err)
+ }
+ //log.Debugf("using inject template from configmap %q", injectConfigMapName)
+ return injectConfig.Template, nil
+}
+
+
+func homeDir() string {
+ if h := os.Getenv("HOME"); h != "" {
+ return h
+ }
+ return os.Getenv("USERPROFILE") // windows
+}
+
+func createInterface(kubeconfig string) (kubernetes.Interface, error) {
+
+ var configContext string
+ restConfig, err := kube.BuildClientConfig(kubeconfig, configContext)
+
+ if err != nil {
+ return nil, err
+ }
+ return kubernetes.NewForConfig(restConfig)
+}
diff --git a/clover/cloverctl/src/cloverkube/main.go b/clover/cloverctl/src/cloverkube/main.go
new file mode 100644
index 0000000..e2854b5
--- /dev/null
+++ b/clover/cloverctl/src/cloverkube/main.go
@@ -0,0 +1,400 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+package cloverkube
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ appsv1 "k8s.io/api/apps/v1"
+ apiv1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "k8s.io/client-go/tools/clientcmd"
+)
+
+func setClient() kubernetes.Interface {
+
+
+ kubeconfig := filepath.Join(
+ os.Getenv("HOME"), ".kube", "config",
+ )
+ config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ // create the clientset
+ clientset, err := kubernetes.NewForConfig(config)
+ if err != nil {
+ panic(err.Error())
+ }
+ return clientset
+}
+
+func setControllerDeploy () (*appsv1.Deployment, *apiv1.Service) {
+
+ deployment := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "clover-controller",
+ },
+ Spec: appsv1.DeploymentSpec{
+ Selector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{
+ "app": "clover-controller",
+ },
+ },
+ Template: apiv1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: map[string]string{
+ "app": "clover-controller",
+ },
+ },
+ Spec: apiv1.PodSpec{
+ Containers: []apiv1.Container{
+ {
+ Name: "clover-controller",
+ Image: "localhost:5000/clover-controller:latest",
+ Ports: []apiv1.ContainerPort{
+ {
+ Name: "redis",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 6379,
+ },
+ {
+ Name: "grpc",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 50054,
+ },
+ {
+ Name: "gprcsecurity",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 50052,
+ },
+ {
+ Name: "cassandra",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 9042,
+ },
+
+
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ service := &apiv1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "clover-controller",
+ Labels: map[string]string{
+ "app": "clover-controller",
+ },
+
+ },
+ Spec: apiv1.ServiceSpec{
+ Selector: map[string]string{
+ "app": "clover-controller",
+ },
+ Type: "NodePort",
+ Ports: []apiv1.ServicePort{
+ {
+ Name: "http",
+ Port: 80,
+ NodePort: 32044,
+ Protocol: "TCP",
+ },
+ },
+ },
+ }
+
+ return deployment, service
+
+}
+
+func setCollectorDeploy () (*appsv1.Deployment, *apiv1.Service) {
+
+ deployment := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "clover-collector",
+ },
+ Spec: appsv1.DeploymentSpec{
+ Selector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{
+ "app": "clover-collector",
+ },
+ },
+ Template: apiv1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: map[string]string{
+ "app": "clover-collector",
+ },
+ },
+ Spec: apiv1.PodSpec{
+ Containers: []apiv1.Container{
+ {
+ Name: "clover-collector",
+ Image: "localhost:5000/clover-collector:latest",
+ Ports: []apiv1.ContainerPort{
+ {
+ Name: "redis",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 6379,
+ },
+ {
+ Name: "grpc",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 50054,
+ },
+ {
+ Name: "prometheus",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 9090,
+ },
+ {
+ Name: "jaeger",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 16686,
+ },
+ {
+ Name: "cassandra",
+ Protocol: apiv1.ProtocolTCP,
+ ContainerPort: 9042,
+ },
+
+
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ service := &apiv1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "clover-collector",
+ Labels: map[string]string{
+ "app": "clover-collector",
+ },
+
+ },
+ Spec: apiv1.ServiceSpec{
+ Selector: map[string]string{
+ "app": "clover-collector",
+ },
+ Ports: []apiv1.ServicePort{
+ {
+ Name: "grpc",
+ Port: 50054,
+ },
+ {
+ Name: "redis",
+ Port: 6379,
+ },
+ {
+ Name: "prometheus",
+ Port: 9090,
+ },
+ {
+ Name: "jaeger",
+ Port: 16686,
+ },
+ {
+ Name: "cassandra",
+ Port: 9042,
+ },
+
+ },
+ },
+ }
+ return deployment, service
+}
+
+func DeployCloverSystem(action string, namespace string) {
+ if action == "create" {
+ // Create clover-system namespace
+ configNamespace("clover-system", "create")
+ // Controller
+ deployment, service := setControllerDeploy()
+ DeployService(deployment, service, namespace)
+ // Collector
+ deployment, service = setCollectorDeploy()
+ DeployService(deployment, service, namespace)
+ } else if action == "delete" {
+ fmt.Println("Deleting clover-system services...\n")
+ DeleteService("clover-controller", namespace)
+ DeleteService("clover-collector", namespace)
+ configNamespace("clover-system", "delete")
+ }
+
+}
+
+func DeleteService(deploy_name string, namespace string) {
+
+ clientset := setClient()
+ deploymentsClient := clientset.AppsV1().Deployments(namespace)
+ servicesClient := clientset.CoreV1().Services(namespace)
+
+ // Delete Deployment
+ deletePolicy := metav1.DeletePropagationForeground
+ if err := deploymentsClient.Delete(deploy_name, &metav1.DeleteOptions{
+ PropagationPolicy: &deletePolicy,
+ }); err != nil {
+ panic(err)
+ }
+ fmt.Printf("Deleted %s deployment\n", deploy_name)
+
+ // Delete Service
+ if err := servicesClient.Delete(deploy_name, &metav1.DeleteOptions{
+ PropagationPolicy: &deletePolicy,
+ }); err != nil {
+ panic(err)
+ }
+ fmt.Printf("Deleted %s service\n", deploy_name)
+}
+
+func DeployService(deployment *appsv1.Deployment, service *apiv1.Service, namespace string) {
+
+ clientset := setClient()
+ deploymentsClient := clientset.AppsV1().Deployments(namespace)
+
+
+ // Create Deployment
+ fmt.Println("Creating deployment...")
+ result, err := deploymentsClient.Create(deployment)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Printf("Created deployment %q.\n", result.GetObjectMeta().GetName())
+
+ // Create Service
+ fmt.Println("Creating service...")
+ servicesClient := clientset.CoreV1().Services(namespace)
+
+ result1, err := servicesClient.Create(service)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Printf("Created service %q.\n", result1.GetObjectMeta().GetName())
+
+}
+
+func configNamespace (name string, action string) {
+ clientset := setClient()
+ nameClient := clientset.CoreV1().Namespaces()
+
+ if action == "create" {
+ namespace := &apiv1.Namespace{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ },
+ }
+ nameClient.Create(namespace)
+ fmt.Printf("Created %s namespace\n", name)
+ } else if action == "delete" {
+ deletePolicy := metav1.DeletePropagationForeground
+ if err := nameClient.Delete(name, &metav1.DeleteOptions{
+ PropagationPolicy: &deletePolicy,
+ }); err != nil {
+ panic(err)
+ }
+ fmt.Printf("Deleted %s namespace\n", name)
+ }
+}
+
+func GetServices() *apiv1.ServiceList {
+
+ clientset := setClient()
+ services, err := clientset.Core().Services("").List(metav1.ListOptions{})
+ for _, service := range services.Items {
+ if err != nil {
+ panic(err.Error())
+ }
+ fmt.Printf(" * SERVICE Name: %s\n", service.GetName())
+ fmt.Printf("Kind: %s\n", service.Kind)
+ fmt.Printf("Labels: %s\n", service.GetLabels())
+ fmt.Printf("Type: %s\n", service.Spec.Type)
+ //fmt.Printf("External IP: %v\n", service.Spec.ExternalIPs)
+ fmt.Printf("Cluster IP: %s\n", service.Spec.ClusterIP)
+
+ for _, port := range service.Spec.Ports {
+ fmt.Printf("Port Name: %s, Port# %d, NodePort: %d\n", port.Name, port.Port, port.NodePort)
+ }
+
+ for _, ip := range service.Status.LoadBalancer.Ingress {
+ fmt.Printf("LB IP: %s \n", ip.IP)
+ }
+ }
+ return services
+}
+
+func GetDeployments(namespace string) []appsv1.Deployment {
+
+ clientset := setClient()
+
+ deploymentsClient := clientset.AppsV1().Deployments(namespace)
+ list, err := deploymentsClient.List(metav1.ListOptions{})
+ if err != nil {
+ panic(err)
+ }
+ for _, d := range list.Items {
+ fmt.Printf(" * %s (%d replicas)\n", d.Name, *d.Spec.Replicas)
+ }
+ return list.Items
+}
+
+func GetServicesPortIP(service_name string) (int32, string) {
+
+ clientset := setClient()
+ services, err := clientset.Core().Services("").List(metav1.ListOptions{})
+ var nodeport int32
+ var ipaddress string
+ nodeport = 0
+ ipaddress = ""
+ for _, service := range services.Items {
+ if err != nil {
+ panic(err.Error())
+ }
+ if service.GetName() == service_name {
+ for _, port := range service.Spec.Ports {
+ if port.NodePort > 0 {
+ nodeport = port.NodePort
+ }
+ }
+ for _, ip := range service.Status.LoadBalancer.Ingress {
+ ipaddress = ip.IP
+ }
+ }
+ }
+
+ return nodeport, ipaddress
+}
+
+func GetPodsIP(pod_name string, namespace string) []string {
+
+ clientset := setClient()
+
+ var ips []string
+ pods, err := clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{})
+ if err != nil {
+ panic(err)
+ }
+ for _, pod := range pods.Items {
+ if strings.Contains(pod.Name, pod_name) {
+ fmt.Println(pod.Name, pod.Status.PodIP)
+ ips = append(ips, pod.Status.PodIP)
+ }
+ }
+
+ return ips
+}
diff --git a/clover/collector/__init__.py b/clover/collector/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/clover/collector/__init__.py
diff --git a/clover/collector/build.sh b/clover/collector/build.sh
new file mode 100755
index 0000000..f305a02
--- /dev/null
+++ b/clover/collector/build.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"}
+IMAGE_NAME=${IMAGE_NAME:-"clover-collector"}
+
+docker build -f docker/Dockerfile -t $IMAGE_NAME .
+docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME
+docker push $IMAGE_PATH/$IMAGE_NAME
diff --git a/clover/collector/db/__init__.py b/clover/collector/db/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/clover/collector/db/__init__.py
diff --git a/clover/collector/db/cassops.py b/clover/collector/db/cassops.py
new file mode 100644
index 0000000..6553cff
--- /dev/null
+++ b/clover/collector/db/cassops.py
@@ -0,0 +1,144 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+from cassandra.cluster import Cluster
+from cassandra.query import BatchStatement
+import logging
+
+CASSANDRA_HOSTS = ['cassandra.default']
+
+
+class CassandraOps:
+
+ def __init__(self, hosts, port=9042, keyspace='visibility'):
+ logging.basicConfig(filename='cassops.log',
+ level=logging.DEBUG)
+ cluster = Cluster(hosts, port=port)
+ self.session = cluster.connect()
+ self.keyspace = keyspace
+
+ def truncate(self, tables=['traces', 'metrics', 'spans']):
+ self.session.set_keyspace(self.keyspace)
+ try:
+ for table in tables:
+ self.session.execute("""
+ TRUNCATE %s
+ """ % table)
+ except Exception as e:
+ logging.debug(e)
+
+ def init_visibility(self):
+ try:
+ self.session.execute("""
+ CREATE KEYSPACE %s
+ WITH replication = { 'class': 'SimpleStrategy',
+ 'replication_factor': '1' }
+ """ % self.keyspace)
+ except Exception as e:
+ logging.debug(e)
+
+ self.session.set_keyspace(self.keyspace)
+
+ try:
+ self.session.execute("""
+ CREATE TABLE IF NOT EXISTS traces (
+ traceid text,
+ processes list<text>,
+ PRIMARY KEY (traceid)
+ )
+ """)
+
+ self.session.execute("""
+ CREATE TABLE IF NOT EXISTS spans (
+ spanid text,
+ traceid text,
+ duration int,
+ start_time int,
+ processid text,
+ operation_name text,
+ node_id text,
+ http_url text,
+ upstream_cluster text,
+ PRIMARY KEY (spanid, traceid)
+ )
+ """)
+
+ self.session.execute("""
+ CREATE TABLE IF NOT EXISTS metrics (
+ m_name text,
+ m_value text,
+ m_time text,
+ service text,
+ monitor_time timestamp,
+ PRIMARY KEY (m_name, monitor_time)
+ )
+ """)
+ except Exception as e:
+ logging.debug(e)
+
+ def set_prepared(self):
+ self.session.set_keyspace(self.keyspace)
+ self.insert_tracing_stmt = self.session.prepare(
+ """
+ INSERT INTO spans (spanid, traceid, duration, operation_name,
+ node_id, http_url, upstream_cluster)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ """
+ )
+ self.insert_metric_stmt = self.session.prepare(
+ """
+ INSERT INTO metrics
+ (m_name, m_value, m_time, service, monitor_time)
+ VALUES (?, ?, ?, ?, toTimestamp(now()))
+ """
+ )
+
+ def set_batch(self):
+ self.batch = BatchStatement()
+
+ def execute_batch(self):
+ self.session.execute(self.batch)
+
+ def insert_tracing(self, table, traceid, s, tags):
+ self.session.set_keyspace(self.keyspace)
+ if 'upstream_cluster' not in tags:
+ logging.debug('NO UPSTREAM_CLUSTER KEY')
+ tags['upstream_cluster'] = 'none'
+ try:
+ self.batch.add(self.insert_tracing_stmt,
+ (s['spanID'], traceid, s['duration'],
+ s['operationName'], tags['node_id'],
+ tags['http.url'], tags['upstream_cluster']))
+ except Exception as e:
+ logging.debug('{} {} {} {} {} {} {}'.format(s['spanID'], traceid,
+ s['duration'], s['operationName'], tags['node_id'],
+ tags['http.url'], tags['upstream_cluster']))
+ logging.debug(e)
+
+ def insert_trace(self, traceid, processes):
+ self.session.set_keyspace(self.keyspace)
+ self.session.execute(
+ """
+ INSERT INTO traces (traceid, processes)
+ VALUES (%s, %s)
+ """,
+ (traceid, processes)
+ )
+
+ def insert_metric(self, m_name, m_value, m_time, service):
+ self.session.set_keyspace(self.keyspace)
+ self.batch.add(self.insert_metric_stmt,
+ (m_name, m_value, m_time, service))
+
+
+def main():
+ cass = CassandraOps(CASSANDRA_HOSTS)
+ cass.init_visibility()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/clover/collector/db/redisops.py b/clover/collector/db/redisops.py
new file mode 100644
index 0000000..e80c417
--- /dev/null
+++ b/clover/collector/db/redisops.py
@@ -0,0 +1,59 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+import redis
+import logging
+
+REDIS_HOST = 'redis'
+# REDIS_HOST = '10.244.0.85'
+
+
+class RedisOps:
+
+ def __init__(self, host=REDIS_HOST):
+ logging.basicConfig(filename='redisops.log',
+ level=logging.DEBUG)
+ try:
+ self.r = redis.StrictRedis(host=host, port=6379, db=0)
+ except Exception as e:
+ logging.debug(e)
+
+ def init_services(self, skey='visibility_services'):
+ service_names = ['http_lb', 'proxy_access_control']
+ for s in service_names:
+ self.r.sadd(skey, s)
+
+ def init_metrics(self, pkey='metric_prefixes', skey='metric_suffixes'):
+ metric_prefixes = ['envoy_cluster_out_', 'envoy_cluster_in_']
+ metric_suffixes = [
+ '_default_svc_cluster_local_http_internal_upstream_rq_2xx',
+ '_default_svc_cluster_local_http_upstream_cx_active']
+ for p in metric_prefixes:
+ self.r.sadd(pkey, p)
+ for s in metric_suffixes:
+ self.r.sadd(skey, s)
+
+ def get_services(self, skey='visibility_services'):
+ services = self.r.smembers(skey)
+ return services
+
+ def get_metrics(self, pkey='metric_prefixes', skey='metric_suffixes'):
+ prefixes = self.r.smembers(pkey)
+ suffixes = self.r.smembers(skey)
+ return prefixes, suffixes
+
+
+def main():
+ r = RedisOps()
+ r.init_services()
+ r.init_metrics()
+ r.get_services()
+ r.get_metrics()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/clover/collector/docker/Dockerfile b/clover/collector/docker/Dockerfile
new file mode 100644
index 0000000..1714420
--- /dev/null
+++ b/clover/collector/docker/Dockerfile
@@ -0,0 +1,30 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+FROM opnfv/clover
+
+ENV REPOS_DIR="/home/opnfv/repos"
+
+# Clover repo
+ENV CLOVER_REPO_DIR="${REPOS_DIR}/clover"
+
+# Install required python packages
+RUN python -m pip install cassandra-driver redis
+
+# Set work directory
+WORKDIR ${CLOVER_REPO_DIR}
+
+COPY /process clover/collector/process
+COPY /grpc clover/collector/grpc
+COPY /db clover/collector/db
+COPY __init__.py clover/collector/__init__.py
+
+RUN pip install .
+
+WORKDIR "${CLOVER_REPO_DIR}/clover/collector"
+
+CMD ./process/grpc_process.sh no_schema_init
diff --git a/clover/collector/grpc/__init__.py b/clover/collector/grpc/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/clover/collector/grpc/__init__.py
diff --git a/clover/collector/grpc/build_proto.sh b/clover/collector/grpc/build_proto.sh
new file mode 100755
index 0000000..44467ad
--- /dev/null
+++ b/clover/collector/grpc/build_proto.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. collector.proto
diff --git a/clover/collector/grpc/collector.proto b/clover/collector/grpc/collector.proto
new file mode 100644
index 0000000..fc8b636
--- /dev/null
+++ b/clover/collector/grpc/collector.proto
@@ -0,0 +1,45 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+syntax = "proto3";
+
+package collector;
+
+// The controller service definition.
+service Controller {
+
+ rpc StopCollector (ConfigCollector) returns (CollectorReply) {}
+ rpc StartCollector (ConfigCollector) returns (CollectorReply) {}
+ rpc InitVisibility (ConfigCassandra) returns (CollectorReply) {}
+ rpc TruncateVisibility (Schemas) returns (CollectorReply) {}
+}
+
+message ConfigCassandra {
+ string cassandra_hosts = 1;
+ int32 cassandra_port = 2;
+}
+
+message ConfigCollector {
+ string t_port = 1;
+ string t_host = 2;
+ string m_port = 3;
+ string m_host = 4;
+ string c_port = 5;
+ string c_hosts = 6;
+ string sinterval = 7;
+}
+
+message Schemas {
+ string schemas = 1;
+ string cassandra_hosts = 2;
+ int32 cassandra_port = 3;
+
+}
+
+message CollectorReply {
+ string message = 1;
+}
diff --git a/clover/collector/grpc/collector_client.py b/clover/collector/grpc/collector_client.py
new file mode 100644
index 0000000..b9e9f67
--- /dev/null
+++ b/clover/collector/grpc/collector_client.py
@@ -0,0 +1,105 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+from __future__ import print_function
+from kubernetes import client, config
+
+import grpc
+import argparse
+import pickle
+
+import collector_pb2
+import collector_pb2_grpc
+
+# This is a basic client script to test server GRPC messaging
+# TODO improve interface overall
+
+
+def run(args, grpc_port='50054'):
+ pod_ip = get_podip('clover-collector')
+ if pod_ip == '':
+ return "Can not find service: {}".format(args['service_name'])
+ collector_grpc = pod_ip + ':' + grpc_port
+ channel = grpc.insecure_channel(collector_grpc)
+ stub = collector_pb2_grpc.ControllerStub(channel)
+
+ if args['cmd'] == 'init':
+ return init_visibility(stub)
+ elif args['cmd'] == 'start':
+ return start_collector(stub)
+ elif args['cmd'] == 'stop':
+ return stop_collector(stub)
+ elif args['cmd'] == 'clean':
+ return clean_visibility(stub)
+ else:
+ return "Invalid command: {}".format(args['cmd'])
+
+
+def get_podip(pod_name):
+ ip = ''
+ if pod_name != '':
+ config.load_kube_config()
+ v1 = client.CoreV1Api()
+ ret = v1.list_pod_for_all_namespaces(watch=False)
+ for i in ret.items:
+ if i.metadata.name.lower().find(pod_name.lower()) != -1:
+ print("Pod IP: {}".format(i.status.pod_ip))
+ ip = i.status.pod_ip
+ return str(ip)
+ return str(ip)
+
+
+def init_visibility(stub):
+ try:
+ cassandra_hosts = pickle.dumps(['cassandra.default'])
+ response = stub.InitVisibility(collector_pb2.ConfigCassandra(
+ cassandra_hosts=cassandra_hosts, cassandra_port=9042))
+ except Exception as e:
+ return e
+ return response.message
+
+
+def clean_visibility(stub):
+ try:
+ cassandra_hosts = pickle.dumps(['cassandra.default'])
+ schemas = pickle.dumps(['spans', 'traces', 'metrics'])
+ response = stub.TruncateVisibility(collector_pb2.Schemas(
+ schemas=schemas, cassandra_hosts=cassandra_hosts,
+ cassandra_port=9042))
+ except Exception as e:
+ return e
+ return response.message
+
+
+def start_collector(stub):
+ try:
+ cassandra_hosts = pickle.dumps(['cassandra.default'])
+ response = stub.StartCollector(collector_pb2.ConfigCollector(
+ t_port='16686', t_host='jaeger-deployment.istio-system',
+ m_port='9090', m_host='prometheus.istio-system',
+ c_port='9042', c_hosts=cassandra_hosts,
+ sinterval='5'))
+ except Exception as e:
+ return e
+ return response.message
+
+
+def stop_collector(stub):
+ try:
+ response = stub.StopCollector(collector_pb2.ConfigCollector())
+ except Exception as e:
+ return e
+ return response.message
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--cmd', required=True,
+ help='Command to execute in collector')
+ args = parser.parse_args()
+ print(run(vars(args)))
diff --git a/clover/collector/grpc/collector_pb2.py b/clover/collector/grpc/collector_pb2.py
new file mode 100644
index 0000000..f67c880
--- /dev/null
+++ b/clover/collector/grpc/collector_pb2.py
@@ -0,0 +1,300 @@
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: collector.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='collector.proto',
+ package='collector',
+ syntax='proto3',
+ serialized_pb=_b('\n\x0f\x63ollector.proto\x12\tcollector\"B\n\x0f\x43onfigCassandra\x12\x17\n\x0f\x63\x61ssandra_hosts\x18\x01 \x01(\t\x12\x16\n\x0e\x63\x61ssandra_port\x18\x02 \x01(\x05\"\x85\x01\n\x0f\x43onfigCollector\x12\x0e\n\x06t_port\x18\x01 \x01(\t\x12\x0e\n\x06t_host\x18\x02 \x01(\t\x12\x0e\n\x06m_port\x18\x03 \x01(\t\x12\x0e\n\x06m_host\x18\x04 \x01(\t\x12\x0e\n\x06\x63_port\x18\x05 \x01(\t\x12\x0f\n\x07\x63_hosts\x18\x06 \x01(\t\x12\x11\n\tsinterval\x18\x07 \x01(\t\"K\n\x07Schemas\x12\x0f\n\x07schemas\x18\x01 \x01(\t\x12\x17\n\x0f\x63\x61ssandra_hosts\x18\x02 \x01(\t\x12\x16\n\x0e\x63\x61ssandra_port\x18\x03 \x01(\x05\"!\n\x0e\x43ollectorReply\x12\x0f\n\x07message\x18\x01 \x01(\t2\xb3\x02\n\nController\x12H\n\rStopCollector\x12\x1a.collector.ConfigCollector\x1a\x19.collector.CollectorReply\"\x00\x12I\n\x0eStartCollector\x12\x1a.collector.ConfigCollector\x1a\x19.collector.CollectorReply\"\x00\x12I\n\x0eInitVisibility\x12\x1a.collector.ConfigCassandra\x1a\x19.collector.CollectorReply\"\x00\x12\x45\n\x12TruncateVisibility\x12\x12.collector.Schemas\x1a\x19.collector.CollectorReply\"\x00\x62\x06proto3')
+)
+
+
+
+
+_CONFIGCASSANDRA = _descriptor.Descriptor(
+ name='ConfigCassandra',
+ full_name='collector.ConfigCassandra',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='cassandra_hosts', full_name='collector.ConfigCassandra.cassandra_hosts', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='cassandra_port', full_name='collector.ConfigCassandra.cassandra_port', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=30,
+ serialized_end=96,
+)
+
+
+_CONFIGCOLLECTOR = _descriptor.Descriptor(
+ name='ConfigCollector',
+ full_name='collector.ConfigCollector',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='t_port', full_name='collector.ConfigCollector.t_port', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='t_host', full_name='collector.ConfigCollector.t_host', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='m_port', full_name='collector.ConfigCollector.m_port', index=2,
+ number=3, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='m_host', full_name='collector.ConfigCollector.m_host', index=3,
+ number=4, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='c_port', full_name='collector.ConfigCollector.c_port', index=4,
+ number=5, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='c_hosts', full_name='collector.ConfigCollector.c_hosts', index=5,
+ number=6, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='sinterval', full_name='collector.ConfigCollector.sinterval', index=6,
+ number=7, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=99,
+ serialized_end=232,
+)
+
+
+_SCHEMAS = _descriptor.Descriptor(
+ name='Schemas',
+ full_name='collector.Schemas',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='schemas', full_name='collector.Schemas.schemas', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='cassandra_hosts', full_name='collector.Schemas.cassandra_hosts', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='cassandra_port', full_name='collector.Schemas.cassandra_port', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=234,
+ serialized_end=309,
+)
+
+
+_COLLECTORREPLY = _descriptor.Descriptor(
+ name='CollectorReply',
+ full_name='collector.CollectorReply',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='message', full_name='collector.CollectorReply.message', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=311,
+ serialized_end=344,
+)
+
+DESCRIPTOR.message_types_by_name['ConfigCassandra'] = _CONFIGCASSANDRA
+DESCRIPTOR.message_types_by_name['ConfigCollector'] = _CONFIGCOLLECTOR
+DESCRIPTOR.message_types_by_name['Schemas'] = _SCHEMAS
+DESCRIPTOR.message_types_by_name['CollectorReply'] = _COLLECTORREPLY
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+ConfigCassandra = _reflection.GeneratedProtocolMessageType('ConfigCassandra', (_message.Message,), dict(
+ DESCRIPTOR = _CONFIGCASSANDRA,
+ __module__ = 'collector_pb2'
+ # @@protoc_insertion_point(class_scope:collector.ConfigCassandra)
+ ))
+_sym_db.RegisterMessage(ConfigCassandra)
+
+ConfigCollector = _reflection.GeneratedProtocolMessageType('ConfigCollector', (_message.Message,), dict(
+ DESCRIPTOR = _CONFIGCOLLECTOR,
+ __module__ = 'collector_pb2'
+ # @@protoc_insertion_point(class_scope:collector.ConfigCollector)
+ ))
+_sym_db.RegisterMessage(ConfigCollector)
+
+Schemas = _reflection.GeneratedProtocolMessageType('Schemas', (_message.Message,), dict(
+ DESCRIPTOR = _SCHEMAS,
+ __module__ = 'collector_pb2'
+ # @@protoc_insertion_point(class_scope:collector.Schemas)
+ ))
+_sym_db.RegisterMessage(Schemas)
+
+CollectorReply = _reflection.GeneratedProtocolMessageType('CollectorReply', (_message.Message,), dict(
+ DESCRIPTOR = _COLLECTORREPLY,
+ __module__ = 'collector_pb2'
+ # @@protoc_insertion_point(class_scope:collector.CollectorReply)
+ ))
+_sym_db.RegisterMessage(CollectorReply)
+
+
+
+_CONTROLLER = _descriptor.ServiceDescriptor(
+ name='Controller',
+ full_name='collector.Controller',
+ file=DESCRIPTOR,
+ index=0,
+ options=None,
+ serialized_start=347,
+ serialized_end=654,
+ methods=[
+ _descriptor.MethodDescriptor(
+ name='StopCollector',
+ full_name='collector.Controller.StopCollector',
+ index=0,
+ containing_service=None,
+ input_type=_CONFIGCOLLECTOR,
+ output_type=_COLLECTORREPLY,
+ options=None,
+ ),
+ _descriptor.MethodDescriptor(
+ name='StartCollector',
+ full_name='collector.Controller.StartCollector',
+ index=1,
+ containing_service=None,
+ input_type=_CONFIGCOLLECTOR,
+ output_type=_COLLECTORREPLY,
+ options=None,
+ ),
+ _descriptor.MethodDescriptor(
+ name='InitVisibility',
+ full_name='collector.Controller.InitVisibility',
+ index=2,
+ containing_service=None,
+ input_type=_CONFIGCASSANDRA,
+ output_type=_COLLECTORREPLY,
+ options=None,
+ ),
+ _descriptor.MethodDescriptor(
+ name='TruncateVisibility',
+ full_name='collector.Controller.TruncateVisibility',
+ index=3,
+ containing_service=None,
+ input_type=_SCHEMAS,
+ output_type=_COLLECTORREPLY,
+ options=None,
+ ),
+])
+_sym_db.RegisterServiceDescriptor(_CONTROLLER)
+
+DESCRIPTOR.services_by_name['Controller'] = _CONTROLLER
+
+# @@protoc_insertion_point(module_scope)
diff --git a/clover/collector/grpc/collector_pb2_grpc.py b/clover/collector/grpc/collector_pb2_grpc.py
new file mode 100644
index 0000000..a7be73c
--- /dev/null
+++ b/clover/collector/grpc/collector_pb2_grpc.py
@@ -0,0 +1,97 @@
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
+import collector_pb2 as collector__pb2
+
+
+class ControllerStub(object):
+ """The controller service definition.
+ """
+
+ def __init__(self, channel):
+ """Constructor.
+
+ Args:
+ channel: A grpc.Channel.
+ """
+ self.StopCollector = channel.unary_unary(
+ '/collector.Controller/StopCollector',
+ request_serializer=collector__pb2.ConfigCollector.SerializeToString,
+ response_deserializer=collector__pb2.CollectorReply.FromString,
+ )
+ self.StartCollector = channel.unary_unary(
+ '/collector.Controller/StartCollector',
+ request_serializer=collector__pb2.ConfigCollector.SerializeToString,
+ response_deserializer=collector__pb2.CollectorReply.FromString,
+ )
+ self.InitVisibility = channel.unary_unary(
+ '/collector.Controller/InitVisibility',
+ request_serializer=collector__pb2.ConfigCassandra.SerializeToString,
+ response_deserializer=collector__pb2.CollectorReply.FromString,
+ )
+ self.TruncateVisibility = channel.unary_unary(
+ '/collector.Controller/TruncateVisibility',
+ request_serializer=collector__pb2.Schemas.SerializeToString,
+ response_deserializer=collector__pb2.CollectorReply.FromString,
+ )
+
+
+class ControllerServicer(object):
+ """The controller service definition.
+ """
+
+ def StopCollector(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+ def StartCollector(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+ def InitVisibility(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+ def TruncateVisibility(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+
+def add_ControllerServicer_to_server(servicer, server):
+ rpc_method_handlers = {
+ 'StopCollector': grpc.unary_unary_rpc_method_handler(
+ servicer.StopCollector,
+ request_deserializer=collector__pb2.ConfigCollector.FromString,
+ response_serializer=collector__pb2.CollectorReply.SerializeToString,
+ ),
+ 'StartCollector': grpc.unary_unary_rpc_method_handler(
+ servicer.StartCollector,
+ request_deserializer=collector__pb2.ConfigCollector.FromString,
+ response_serializer=collector__pb2.CollectorReply.SerializeToString,
+ ),
+ 'InitVisibility': grpc.unary_unary_rpc_method_handler(
+ servicer.InitVisibility,
+ request_deserializer=collector__pb2.ConfigCassandra.FromString,
+ response_serializer=collector__pb2.CollectorReply.SerializeToString,
+ ),
+ 'TruncateVisibility': grpc.unary_unary_rpc_method_handler(
+ servicer.TruncateVisibility,
+ request_deserializer=collector__pb2.Schemas.FromString,
+ response_serializer=collector__pb2.CollectorReply.SerializeToString,
+ ),
+ }
+ generic_handler = grpc.method_handlers_generic_handler(
+ 'collector.Controller', rpc_method_handlers)
+ server.add_generic_rpc_handlers((generic_handler,))
diff --git a/clover/collector/grpc/collector_server.py b/clover/collector/grpc/collector_server.py
new file mode 100644
index 0000000..c2eb221
--- /dev/null
+++ b/clover/collector/grpc/collector_server.py
@@ -0,0 +1,98 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+
+from concurrent import futures
+from clover.collector.db.cassops import CassandraOps
+import time
+import sys
+import grpc
+import subprocess
+import pickle
+import logging
+import collector_pb2
+import collector_pb2_grpc
+
+
+_ONE_DAY_IN_SECONDS = 60 * 60 * 24
+GRPC_PORT = '[::]:50054'
+
+
+class Controller(collector_pb2_grpc.ControllerServicer):
+
+ def __init__(self, init_visibility):
+ logging.basicConfig(filename='collector_server.log',
+ level=logging.DEBUG)
+ self.collector = 0
+ if init_visibility == 'set_schemas':
+ cassandra_hosts = pickle.dumps(['cassandra.default'])
+ self.InitVisibility(collector_pb2.ConfigCassandra(
+ cassandra_port=9042, cassandra_hosts=cassandra_hosts), "")
+
+ def StopCollector(self, r, context):
+ try:
+ subprocess.Popen.kill(self.collector)
+ msg = "Stopped collector on pid: {}".format(self.collector.pid)
+ except Exception as e:
+ logging.debug(e)
+ msg = "Failed to stop collector"
+ return collector_pb2.CollectorReply(message=msg)
+
+ def StartCollector(self, r, context):
+ try:
+ self.collector = subprocess.Popen(
+ ["python", "process/collect.py",
+ "-sinterval={}".format(r.sinterval),
+ "-c_port={}".format(r.c_port),
+ "-t_port={}".format(r.t_port), "-t_host={}".format(r.t_host),
+ "-m_port={}".format(r.m_port), "-m_host={}".format(r.m_host),
+ "-c_hosts={}".format(pickle.loads(r.c_hosts)), "&"],
+ shell=False)
+ msg = "Started collector on pid: {}".format(self.collector.pid)
+ except Exception as e:
+ logging.debug(e)
+ msg = e
+ return collector_pb2.CollectorReply(message=msg)
+
+ def InitVisibility(self, r, context):
+ try:
+ cass = CassandraOps(pickle.loads(r.cassandra_hosts),
+ r.cassandra_port)
+ cass.init_visibility()
+ msg = "Added visibility schemas in cassandra"
+ except Exception as e:
+ logging.debug(e)
+ msg = "Failed to initialize cassandra"
+ return collector_pb2.CollectorReply(message=msg)
+
+ def TruncateVisibility(self, r, context):
+ try:
+ cass = CassandraOps(pickle.loads(r.cassandra_hosts),
+ r.cassandra_port)
+ cass.truncate(pickle.loads(r.schemas))
+ msg = "Truncated visibility tables"
+ except Exception as e:
+ logging.debug(e)
+ msg = "Failed to truncate visibility"
+ return collector_pb2.CollectorReply(message=msg)
+
+
+def serve(init_visibility):
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+ collector_pb2_grpc.add_ControllerServicer_to_server(
+ Controller(init_visibility), server)
+ server.add_insecure_port(GRPC_PORT)
+ server.start()
+ try:
+ while True:
+ time.sleep(_ONE_DAY_IN_SECONDS)
+ except KeyboardInterrupt:
+ server.stop(0)
+
+
+if __name__ == '__main__':
+ serve(sys.argv[1])
diff --git a/clover/collector/process/__init__.py b/clover/collector/process/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/clover/collector/process/__init__.py
diff --git a/clover/collector/process/collect.py b/clover/collector/process/collect.py
new file mode 100644
index 0000000..d8beb49
--- /dev/null
+++ b/clover/collector/process/collect.py
@@ -0,0 +1,162 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+from clover.tracing.tracing import Tracing
+from clover.monitoring.monitoring import Monitoring
+from clover.collector.db.cassops import CassandraOps
+from clover.collector.db.redisops import RedisOps
+
+# import pprint
+import time
+import argparse
+import logging
+import ast
+
+TRACING_SERVICES = ['istio-ingress']
+TRACING_PORT = "16686"
+MONITORING_PORT = "9090"
+CASSANDRA_PORT = 9042 # Provide as integer
+MONITORING_HOST = "prometheus.istio-system"
+TRACING_HOST = "jaeger-deployment.istio-system"
+CASSANDRA_HOSTS = ['cassandra.default']
+
+
+class Collector:
+
+ def __init__(self, t_port, t_host, m_port, m_host, c_port, c_hosts):
+ logging.basicConfig(filename='collector.log', level=logging.DEBUG)
+ try:
+ self.t = Tracing(t_host, t_port, '', False)
+ monitoring_url = "http://{}:{}".format(m_host, m_port)
+ self.m = Monitoring(monitoring_url)
+ self.c = CassandraOps(c_hosts, int(c_port))
+ self.c.set_prepared()
+ self.r = RedisOps()
+ except Exception as e:
+ logging.debug(e)
+
+ # Toplevel tracing retrieval and batch insert
+ def get_tracing(self, services, time_back=20):
+ self.c.set_batch()
+ for service in services:
+ traces = self.t.getTraces(service, time_back)
+ try:
+ self.set_tracing(traces)
+ except Exception as e:
+ logging.debug(e)
+ self.c.execute_batch()
+
+ # Insert to cassandra visibility traces and spans tables
+ def set_tracing(self, trace):
+ for traces in trace['data']:
+ for spans in traces['spans']:
+ span = {}
+ span['spanID'] = spans['spanID']
+ span['duration'] = spans['duration']
+ span['startTime'] = spans['startTime']
+ span['operationName'] = spans['operationName']
+ tag = {}
+ for tags in spans['tags']:
+ tag[tags['key']] = tags['value']
+ self.c.insert_tracing('spans', traces['traceID'],
+ span, tag)
+ process_list = []
+ for p in traces['processes']:
+ process_list.append(p)
+ service_names = []
+ for pname in process_list:
+ service_names.append(traces['processes'][pname]['serviceName'])
+ self.c.insert_trace(traces['traceID'], service_names)
+
+ # Insert to cassandra visibility metrics table
+ def get_monitoring(self):
+
+ # Fetch collector service/metric lists from redis
+ service_names = self.r.get_services()
+ metric_prefixes, metric_suffixes = self.r.get_metrics()
+
+ self.c.set_batch()
+ for sname in service_names:
+ for prefix in metric_prefixes:
+ for suffix in metric_suffixes:
+ try:
+ metric_name = prefix + sname + suffix
+ query_params = {
+ "type": "instant",
+ "query": metric_name
+ }
+ data = self.m.query(query_params)
+ m_value = data['data']['result'][0]['value'][1]
+ m_time = data['data']['result'][0]['value'][0]
+ mn = data['data']['result'][0]['metric']['__name__']
+ self.c.insert_metric(mn, m_value, str(m_time), sname)
+ except Exception as e:
+ logging.debug(e)
+ self.c.execute_batch()
+
+ # TODO add batch retrieval for monitoring metrics
+ # query_range_param = {
+ # "type": "range",
+ # "query": "tbd",
+ # "start": "60m",
+ # "end": "5m",
+ # "step": "30s"
+ # }
+ # data = self.m.query(query_range_param)
+ # pp = pprint.PrettyPrinter(indent=2)
+ # pp.pprint(data)
+
+
+def main(args):
+ if isinstance(args['c_hosts'], basestring):
+ ch = ast.literal_eval(args['c_hosts'])
+ else:
+ ch = args['c_hosts']
+
+ c = Collector(args['t_port'], args['t_host'], args['m_port'],
+ args['m_host'], args['c_port'], ch)
+
+ # Collector loop
+ loop = True
+ while loop:
+ try:
+ c.get_tracing(args['t_services'])
+ c.get_monitoring()
+ time.sleep(int(args['sinterval']))
+ except KeyboardInterrupt:
+ loop = False
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '-sinterval', default=5,
+ help='Sample interval for collector loop')
+ parser.add_argument(
+ '-t_port', default=TRACING_PORT,
+ help='Port to access Jaeger tracing')
+ parser.add_argument(
+ '-m_port', default=MONITORING_PORT,
+ help='Port to access Prometheus monitoring')
+ parser.add_argument(
+ '-t_host', default=TRACING_HOST,
+ help='Host to access Jaeger tracing')
+ parser.add_argument(
+ '-m_host', default=MONITORING_HOST,
+ help='Host to access Prometheus monitoring')
+ parser.add_argument(
+ '-c_hosts', default=CASSANDRA_HOSTS,
+ help='Host(s) to access Cassandra cluster')
+ parser.add_argument(
+ '-c_port', default=CASSANDRA_PORT,
+ help='Port to access Cassandra cluster')
+ parser.add_argument(
+ '-t_services', default=TRACING_SERVICES,
+ help='Collect services on this list of services')
+
+ args, unknown = parser.parse_known_args()
+ print(main(vars(args)))
diff --git a/clover/collector/process/grpc_process.sh b/clover/collector/process/grpc_process.sh
new file mode 100755
index 0000000..30e0171
--- /dev/null
+++ b/clover/collector/process/grpc_process.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+python grpc/collector_server.py test1
diff --git a/clover/collector/yaml/manifest.template b/clover/collector/yaml/manifest.template
new file mode 100644
index 0000000..c7aa3e7
--- /dev/null
+++ b/clover/collector/yaml/manifest.template
@@ -0,0 +1,43 @@
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: {{ deploy_name }}
+ labels:
+ app: {{ deploy_name }}
+spec:
+ template:
+ metadata:
+ labels:
+ app: {{ deploy_name }}
+ spec:
+ containers:
+ - name: {{ deploy_name }}
+ image: {{ image_path }}/{{ image_name }}:{{ image_tag }}
+ ports:
+ - containerPort: {{ grpc_port }}
+ - containerPort: {{ redis_port }}
+ - containerPort: {{ monitor_port }}
+ - containerPort: {{ trace_port }}
+ - containerPort: {{ cass_port }}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ deploy_name }}
+ labels:
+ app: {{ deploy_name }}
+spec:
+ ports:
+ - port: {{ grpc_port }}
+ name: grpc
+ - port: {{ redis_port }}
+ name: redis
+ - port: {{ trace_port }}
+ name: jaeger-deployment
+ - port: {{ monitor_port }}
+ name: prometheus
+ - port: {{ cass_port }}
+ name: cassandra
+ selector:
+ app: {{ deploy_name }}
diff --git a/clover/collector/yaml/render_yaml.py b/clover/collector/yaml/render_yaml.py
new file mode 100644
index 0000000..c1d8be7
--- /dev/null
+++ b/clover/collector/yaml/render_yaml.py
@@ -0,0 +1,73 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+import argparse
+
+from jinja2 import Template
+
+
+def render_yaml(args):
+ template_file = 'manifest.template'
+ out_file = args['deploy_name'] + '.yaml'
+
+ try:
+ with open(template_file) as f:
+ tmpl = Template(f.read())
+ output = tmpl.render(
+ image_path=args['image_path'],
+ image_name=args['image_name'],
+ image_tag=args['image_tag'],
+ deploy_name=args['deploy_name'],
+ grpc_port=args['grpc_port'],
+ monitor_port=args['monitor_port'],
+ redis_port=args['redis_port'],
+ cass_port=args['cass_port'],
+ trace_port=args['trace_port']
+ )
+ with open(out_file, "wb") as fh:
+ fh.write(output)
+ return "Generated manifest for {}".format(args['deploy_name'])
+ except Exception as e:
+ print(e)
+ return "Unable to generate manifest for {}".format(
+ args['deploy_name'])
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--image_name', default='clover-collector',
+ help='The image name to use')
+ parser.add_argument(
+ # '--image_path', default='opnfv',
+ '--image_path', default='localhost:5000',
+ help='The path to the image to use')
+ parser.add_argument(
+ # '--image_tag', default='opnfv-6.0.0',
+ '--image_tag', default='latest',
+ help='The image tag to use')
+ parser.add_argument(
+ '--deploy_name', default='clover-collector',
+ help='The k8s deploy name to use')
+ parser.add_argument(
+ '--redis_port', default='6379',
+ help='The redis port to connect for management')
+ parser.add_argument(
+ '--monitor_port', default='9090',
+ help='The Prometheus monitoring port')
+ parser.add_argument(
+ '--grpc_port', default='50054',
+ help='The GRPC server port for collector management')
+ parser.add_argument(
+ '--trace_port', default='16686',
+ help='The Jaeger tracing port')
+ parser.add_argument(
+ '--cass_port', default='9042',
+ help='The Cassandra port')
+
+ args = parser.parse_args()
+ print(render_yaml(vars(args)))
diff --git a/clover/logging/install/elasticsearch-statefulset-service.yaml b/clover/logging/install/elasticsearch-statefulset-service.yaml
new file mode 100644
index 0000000..0fcc832
--- /dev/null
+++ b/clover/logging/install/elasticsearch-statefulset-service.yaml
@@ -0,0 +1,129 @@
+# RBAC authn and authz
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - "services"
+ - "namespaces"
+ - "endpoints"
+ verbs:
+ - "get"
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ namespace: kube-system
+ name: elasticsearch-logging
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+subjects:
+- kind: ServiceAccount
+ name: elasticsearch-logging
+ namespace: kube-system
+ apiGroup: ""
+roleRef:
+ kind: ClusterRole
+ name: elasticsearch-logging
+ apiGroup: ""
+---
+# Elasticsearch deployment itself
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+spec:
+ serviceName: elasticsearch-logging
+ replicas: 2
+ selector:
+ matchLabels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ template:
+ metadata:
+ labels:
+ k8s-app: elasticsearch-logging
+ version: v5.6.4
+ kubernetes.io/cluster-service: "true"
+ spec:
+ serviceAccountName: elasticsearch-logging
+ containers:
+ - image: k8s.gcr.io/elasticsearch:v5.6.4
+ name: elasticsearch-logging
+ resources:
+ # need more cpu upon initialization, therefore burstable class
+ limits:
+ cpu: 1000m
+ requests:
+ cpu: 100m
+ ports:
+ - containerPort: 9200
+ name: db
+ protocol: TCP
+ - containerPort: 9300
+ name: transport
+ protocol: TCP
+ volumeMounts:
+ - name: elasticsearch-logging
+ mountPath: /data
+ env:
+ - name: "NAMESPACE"
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
+ volumes:
+ - name: elasticsearch-logging
+ emptyDir: {}
+ # Elasticsearch requires vm.max_map_count to be at least 262144.
+ # If your OS already sets up this number to a higher value, feel free
+ # to remove this init container.
+ initContainers:
+ - image: alpine:3.6
+ command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
+ name: elasticsearch-logging-init
+ securityContext:
+ privileged: true
+---
+# Elasticsearch Service
+apiVersion: v1
+kind: Service
+metadata:
+ name: elasticsearch-logging
+ namespace: kube-system
+ labels:
+ k8s-app: elasticsearch-logging
+ kubernetes.io/cluster-service: "true"
+ addonmanager.kubernetes.io/mode: Reconcile
+ kubernetes.io/name: "Elasticsearch"
+spec:
+ ports:
+ - port: 9200
+ protocol: TCP
+ targetPort: db
+ selector:
+ k8s-app: elasticsearch-logging
diff --git a/clover/logging/install/fluentd-daemonset-elasticsearch-rbac.yaml b/clover/logging/install/fluentd-daemonset-elasticsearch-rbac.yaml
index 445db26..8131ef5 100644
--- a/clover/logging/install/fluentd-daemonset-elasticsearch-rbac.yaml
+++ b/clover/logging/install/fluentd-daemonset-elasticsearch-rbac.yaml
@@ -63,7 +63,7 @@ spec:
image: fluent/fluentd-kubernetes-daemonset:elasticsearch
env:
- name: FLUENT_ELASTICSEARCH_HOST
- value: "elasticsearch.logging"
+ value: "elasticsearch-logging"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
diff --git a/clover/monitoring/monitoring.py b/clover/monitoring/monitoring.py
index 9726fd1..ec97e82 100644
--- a/clover/monitoring/monitoring.py
+++ b/clover/monitoring/monitoring.py
@@ -90,8 +90,9 @@ class Monitoring(object):
print("query %s %s, status=%s, size=%d, dur=%.3f" % \
(self.host, query_params["query"], resp.status_code, len(resp.text), dur))
- pp = pprint.PrettyPrinter(indent=2)
- pp.pprint(resp.json())
+ #pp = pprint.PrettyPrinter(indent=2)
+ ##pp.pprint(resp.json())
+ return resp.json()
except Exception as e:
print("ERROR: Could not query prometheus instance %s. \n %s" % (url, e))
diff --git a/clover/test/fraser_a_b_test.py b/clover/test/fraser_a_b_test.py
index cfbc79f..2270e04 100644
--- a/clover/test/fraser_a_b_test.py
+++ b/clover/test/fraser_a_b_test.py
@@ -218,6 +218,8 @@ def main(argv):
test_id = uuid.uuid4()
rr.set_route_rules(test_id)
tracing.setTest(test_id)
+ # wait 1 sec to avoid missing the first test result
+ time.sleep(1)
try:
output = subprocess.check_output(cmd, shell=True)
except subprocess.CalledProcessError, e:
diff --git a/clover/tools/jmeter/build_master.sh b/clover/tools/jmeter/build_master.sh
new file mode 100755
index 0000000..5c5459a
--- /dev/null
+++ b/clover/tools/jmeter/build_master.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"}
+IMAGE_NAME=${IMAGE_NAME:-"clover-jmeter-master"}
+
+docker build -f jmeter-master/Dockerfile -t $IMAGE_NAME .
+docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME
+docker push $IMAGE_PATH/$IMAGE_NAME
diff --git a/clover/tools/jmeter/build_slave.sh b/clover/tools/jmeter/build_slave.sh
new file mode 100755
index 0000000..1651c55
--- /dev/null
+++ b/clover/tools/jmeter/build_slave.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"}
+IMAGE_NAME=${IMAGE_NAME:-"clover-jmeter-slave"}
+
+docker build -f jmeter-slave/Dockerfile -t $IMAGE_NAME .
+docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME
+docker push $IMAGE_PATH/$IMAGE_NAME
diff --git a/clover/tools/jmeter/jmeter-master/Dockerfile b/clover/tools/jmeter/jmeter-master/Dockerfile
new file mode 100644
index 0000000..da0e474
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/Dockerfile
@@ -0,0 +1,29 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+FROM java:8
+
+RUN wget http://apache.mirrors.hoobly.com//jmeter/binaries/apache-jmeter-4.0.tgz
+RUN tar -xvzf apache-jmeter-4.0.tgz
+RUN rm apache-jmeter-4.0.tgz
+RUN mv apache-jmeter-4.0 /jmeter
+ENV JMETER_HOME /jmeter
+ENV PATH $JMETER_HOME/bin:$PATH
+COPY rmi_keystore.jks $JMETER_HOME/bin
+
+RUN apt update && apt install -y python-setuptools python-dev python-pip
+RUN python -m pip install --upgrade pip
+RUN python -m pip install enum34 futures cython
+RUN python -m pip install grpcio protobuf jinja2
+
+WORKDIR /jmeter/bin
+
+COPY jmeter-master/process process
+COPY jmeter-master/grpc grpc
+COPY jmeter-master/tests tests
+
+CMD ./process/grpc_process.sh no_init
diff --git a/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh b/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh
new file mode 100755
index 0000000..52dfd0a
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/grpc/build_proto.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. jmeter.proto
diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto b/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto
new file mode 100644
index 0000000..7213faa
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter.proto
@@ -0,0 +1,43 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+
+syntax = "proto3";
+
+package jmeter;
+
+// The controller service definition.
+service Controller {
+
+ rpc GenTest (ConfigJmeter) returns (JmeterReply) {}
+ rpc StartTest (TestParams) returns (JmeterReply) {}
+ rpc GetResults (JResults) returns (JmeterReply) {}
+}
+
+message TestParams {
+ string num_slaves = 1;
+ string test_plan = 2;
+ string slave_ips = 3;
+}
+
+message ConfigJmeter {
+ string url_list = 1;
+ string num_threads = 2;
+ string url_names = 3;
+ string url_protocols = 4;
+ string url_methods = 5;
+ string loops = 6;
+ string ramp_time = 7;
+}
+
+message JmeterReply {
+ string message = 1;
+}
+
+message JResults {
+ string r_format = 1;
+ string r_file = 2;
+}
diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py
new file mode 100644
index 0000000..e4a75fd
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2.py
@@ -0,0 +1,291 @@
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: jmeter.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='jmeter.proto',
+ package='jmeter',
+ syntax='proto3',
+ serialized_pb=_b('\n\x0cjmeter.proto\x12\x06jmeter\"F\n\nTestParams\x12\x12\n\nnum_slaves\x18\x01 \x01(\t\x12\x11\n\ttest_plan\x18\x02 \x01(\t\x12\x11\n\tslave_ips\x18\x03 \x01(\t\"\x96\x01\n\x0c\x43onfigJmeter\x12\x10\n\x08url_list\x18\x01 \x01(\t\x12\x13\n\x0bnum_threads\x18\x02 \x01(\t\x12\x11\n\turl_names\x18\x03 \x01(\t\x12\x15\n\rurl_protocols\x18\x04 \x01(\t\x12\x13\n\x0burl_methods\x18\x05 \x01(\t\x12\r\n\x05loops\x18\x06 \x01(\t\x12\x11\n\tramp_time\x18\x07 \x01(\t\"\x1e\n\x0bJmeterReply\x12\x0f\n\x07message\x18\x01 \x01(\t\",\n\x08JResults\x12\x10\n\x08r_format\x18\x01 \x01(\t\x12\x0e\n\x06r_file\x18\x02 \x01(\t2\xb3\x01\n\nController\x12\x36\n\x07GenTest\x12\x14.jmeter.ConfigJmeter\x1a\x13.jmeter.JmeterReply\"\x00\x12\x36\n\tStartTest\x12\x12.jmeter.TestParams\x1a\x13.jmeter.JmeterReply\"\x00\x12\x35\n\nGetResults\x12\x10.jmeter.JResults\x1a\x13.jmeter.JmeterReply\"\x00\x62\x06proto3')
+)
+
+
+
+
+_TESTPARAMS = _descriptor.Descriptor(
+ name='TestParams',
+ full_name='jmeter.TestParams',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='num_slaves', full_name='jmeter.TestParams.num_slaves', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='test_plan', full_name='jmeter.TestParams.test_plan', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='slave_ips', full_name='jmeter.TestParams.slave_ips', index=2,
+ number=3, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=24,
+ serialized_end=94,
+)
+
+
+_CONFIGJMETER = _descriptor.Descriptor(
+ name='ConfigJmeter',
+ full_name='jmeter.ConfigJmeter',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='url_list', full_name='jmeter.ConfigJmeter.url_list', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='num_threads', full_name='jmeter.ConfigJmeter.num_threads', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='url_names', full_name='jmeter.ConfigJmeter.url_names', index=2,
+ number=3, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='url_protocols', full_name='jmeter.ConfigJmeter.url_protocols', index=3,
+ number=4, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='url_methods', full_name='jmeter.ConfigJmeter.url_methods', index=4,
+ number=5, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='loops', full_name='jmeter.ConfigJmeter.loops', index=5,
+ number=6, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='ramp_time', full_name='jmeter.ConfigJmeter.ramp_time', index=6,
+ number=7, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=97,
+ serialized_end=247,
+)
+
+
+_JMETERREPLY = _descriptor.Descriptor(
+ name='JmeterReply',
+ full_name='jmeter.JmeterReply',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='message', full_name='jmeter.JmeterReply.message', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=249,
+ serialized_end=279,
+)
+
+
+_JRESULTS = _descriptor.Descriptor(
+ name='JResults',
+ full_name='jmeter.JResults',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='r_format', full_name='jmeter.JResults.r_format', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='r_file', full_name='jmeter.JResults.r_file', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None, file=DESCRIPTOR),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=281,
+ serialized_end=325,
+)
+
+DESCRIPTOR.message_types_by_name['TestParams'] = _TESTPARAMS
+DESCRIPTOR.message_types_by_name['ConfigJmeter'] = _CONFIGJMETER
+DESCRIPTOR.message_types_by_name['JmeterReply'] = _JMETERREPLY
+DESCRIPTOR.message_types_by_name['JResults'] = _JRESULTS
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+TestParams = _reflection.GeneratedProtocolMessageType('TestParams', (_message.Message,), dict(
+ DESCRIPTOR = _TESTPARAMS,
+ __module__ = 'jmeter_pb2'
+ # @@protoc_insertion_point(class_scope:jmeter.TestParams)
+ ))
+_sym_db.RegisterMessage(TestParams)
+
+ConfigJmeter = _reflection.GeneratedProtocolMessageType('ConfigJmeter', (_message.Message,), dict(
+ DESCRIPTOR = _CONFIGJMETER,
+ __module__ = 'jmeter_pb2'
+ # @@protoc_insertion_point(class_scope:jmeter.ConfigJmeter)
+ ))
+_sym_db.RegisterMessage(ConfigJmeter)
+
+JmeterReply = _reflection.GeneratedProtocolMessageType('JmeterReply', (_message.Message,), dict(
+ DESCRIPTOR = _JMETERREPLY,
+ __module__ = 'jmeter_pb2'
+ # @@protoc_insertion_point(class_scope:jmeter.JmeterReply)
+ ))
+_sym_db.RegisterMessage(JmeterReply)
+
+JResults = _reflection.GeneratedProtocolMessageType('JResults', (_message.Message,), dict(
+ DESCRIPTOR = _JRESULTS,
+ __module__ = 'jmeter_pb2'
+ # @@protoc_insertion_point(class_scope:jmeter.JResults)
+ ))
+_sym_db.RegisterMessage(JResults)
+
+
+
+_CONTROLLER = _descriptor.ServiceDescriptor(
+ name='Controller',
+ full_name='jmeter.Controller',
+ file=DESCRIPTOR,
+ index=0,
+ options=None,
+ serialized_start=328,
+ serialized_end=507,
+ methods=[
+ _descriptor.MethodDescriptor(
+ name='GenTest',
+ full_name='jmeter.Controller.GenTest',
+ index=0,
+ containing_service=None,
+ input_type=_CONFIGJMETER,
+ output_type=_JMETERREPLY,
+ options=None,
+ ),
+ _descriptor.MethodDescriptor(
+ name='StartTest',
+ full_name='jmeter.Controller.StartTest',
+ index=1,
+ containing_service=None,
+ input_type=_TESTPARAMS,
+ output_type=_JMETERREPLY,
+ options=None,
+ ),
+ _descriptor.MethodDescriptor(
+ name='GetResults',
+ full_name='jmeter.Controller.GetResults',
+ index=2,
+ containing_service=None,
+ input_type=_JRESULTS,
+ output_type=_JMETERREPLY,
+ options=None,
+ ),
+])
+_sym_db.RegisterServiceDescriptor(_CONTROLLER)
+
+DESCRIPTOR.services_by_name['Controller'] = _CONTROLLER
+
+# @@protoc_insertion_point(module_scope)
diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py
new file mode 100644
index 0000000..4df110d
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_pb2_grpc.py
@@ -0,0 +1,80 @@
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+import grpc
+
+import jmeter_pb2 as jmeter__pb2
+
+
+class ControllerStub(object):
+ """The controller service definition.
+ """
+
+ def __init__(self, channel):
+ """Constructor.
+
+ Args:
+ channel: A grpc.Channel.
+ """
+ self.GenTest = channel.unary_unary(
+ '/jmeter.Controller/GenTest',
+ request_serializer=jmeter__pb2.ConfigJmeter.SerializeToString,
+ response_deserializer=jmeter__pb2.JmeterReply.FromString,
+ )
+ self.StartTest = channel.unary_unary(
+ '/jmeter.Controller/StartTest',
+ request_serializer=jmeter__pb2.TestParams.SerializeToString,
+ response_deserializer=jmeter__pb2.JmeterReply.FromString,
+ )
+ self.GetResults = channel.unary_unary(
+ '/jmeter.Controller/GetResults',
+ request_serializer=jmeter__pb2.JResults.SerializeToString,
+ response_deserializer=jmeter__pb2.JmeterReply.FromString,
+ )
+
+
+class ControllerServicer(object):
+ """The controller service definition.
+ """
+
+ def GenTest(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+ def StartTest(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+ def GetResults(self, request, context):
+ # missing associated documentation comment in .proto file
+ pass
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
+
+def add_ControllerServicer_to_server(servicer, server):
+ rpc_method_handlers = {
+ 'GenTest': grpc.unary_unary_rpc_method_handler(
+ servicer.GenTest,
+ request_deserializer=jmeter__pb2.ConfigJmeter.FromString,
+ response_serializer=jmeter__pb2.JmeterReply.SerializeToString,
+ ),
+ 'StartTest': grpc.unary_unary_rpc_method_handler(
+ servicer.StartTest,
+ request_deserializer=jmeter__pb2.TestParams.FromString,
+ response_serializer=jmeter__pb2.JmeterReply.SerializeToString,
+ ),
+ 'GetResults': grpc.unary_unary_rpc_method_handler(
+ servicer.GetResults,
+ request_deserializer=jmeter__pb2.JResults.FromString,
+ response_serializer=jmeter__pb2.JmeterReply.SerializeToString,
+ ),
+ }
+ generic_handler = grpc.method_handlers_generic_handler(
+ 'jmeter.Controller', rpc_method_handlers)
+ server.add_generic_rpc_handlers((generic_handler,))
diff --git a/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py b/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py
new file mode 100644
index 0000000..cef180c
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/grpc/jmeter_server.py
@@ -0,0 +1,118 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+from concurrent import futures
+from jinja2 import Template
+from urlparse import urlparse
+import time
+import sys
+import grpc
+import subprocess
+import pickle
+import logging
+import jmeter_pb2
+import jmeter_pb2_grpc
+
+
+_ONE_DAY_IN_SECONDS = 60 * 60 * 24
+GRPC_PORT = '[::]:50054'
+
+
+class Controller(jmeter_pb2_grpc.ControllerServicer):
+
+ def __init__(self, init_jmeter):
+ logging.basicConfig(filename='jmeter_server.log',
+ level=logging.DEBUG)
+ self.jmeter = 0
+ if init_jmeter == 'init':
+ print('init test')
+
+ def GenTest(self, r, context):
+ try:
+ out_file = 'tests/test.jmx'
+ template_file = 'tests/jmx.template'
+ unames = pickle.loads(r.url_names)
+ umethods = pickle.loads(r.url_methods)
+ ulist = []
+ for url in pickle.loads(r.url_list):
+ u = urlparse(url)
+ d = {}
+ d['domain'] = u.hostname
+ if u.port:
+ d['port'] = u.port
+ else:
+ d['port'] = 80
+ if u.path == '':
+ d['path'] = '/'
+ else:
+ d['path'] = u.path
+ ulist.append(d)
+
+ with open(template_file) as f:
+ tmpl = Template(f.read())
+ output = tmpl.render(
+ num_threads=r.num_threads,
+ url_names=unames,
+ url_methods=umethods,
+ ramp_time=r.ramp_time,
+ loops=r.loops,
+ url_list=ulist
+ )
+ with open(out_file, "wb") as fh:
+ fh.write(output)
+ msg = 'Generated test plan'
+ except Exception as e:
+ logging.debug(e)
+ msg = "Failed to generate test plan"
+ return jmeter_pb2.JmeterReply(message=msg)
+
+ def StartTest(self, r, context):
+ try:
+ if r.num_slaves == '0':
+ self.jmeter = subprocess.Popen(
+ ["jmeter", "-n", "-t", "tests/test.jmx", "-l",
+ "default.jtl"], shell=False)
+ else:
+ slave_arg = "-R" + r.slave_ips
+ self.jmeter = subprocess.Popen(
+ ["jmeter", "-n", "-t", "tests/test.jmx", slave_arg, "-l",
+ "default.jtl"], shell=False)
+ msg = "Started jmeter on pid: {}".format(self.jmeter.pid)
+ except Exception as e:
+ logging.debug(e)
+ msg = e
+ return jmeter_pb2.JmeterReply(message=msg)
+
+ def GetResults(self, r, context):
+ try:
+ if r.r_file == 'log':
+ r_file = 'jmeter.log'
+ else:
+ r_file = 'default.jtl'
+ f = open(r_file, 'r')
+ msg = "Retrieved all results\n" + f.read()
+ except Exception as e:
+ logging.debug(e)
+ msg = "Failed to retrieve results"
+ return jmeter_pb2.JmeterReply(message=msg)
+
+
+def serve(init_jmeter):
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+ jmeter_pb2_grpc.add_ControllerServicer_to_server(
+ Controller(init_jmeter), server)
+ server.add_insecure_port(GRPC_PORT)
+ server.start()
+ try:
+ while True:
+ time.sleep(_ONE_DAY_IN_SECONDS)
+ except KeyboardInterrupt:
+ server.stop(0)
+
+
+if __name__ == '__main__':
+ serve(sys.argv[1])
diff --git a/clover/tools/jmeter/jmeter-master/process/grpc_process.sh b/clover/tools/jmeter/jmeter-master/process/grpc_process.sh
new file mode 100755
index 0000000..0f14d7c
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/process/grpc_process.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+
+python grpc/jmeter_server.py test1
diff --git a/clover/tools/jmeter/jmeter-master/tests/jmx.template b/clover/tools/jmeter/jmeter-master/tests/jmx.template
new file mode 100644
index 0000000..1a6fa95
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-master/tests/jmx.template
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jmeterTestPlan version="1.2" properties="4.0" jmeter="4.0 r1823414">
+ <hashTree>
+ <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
+ <stringProp name="TestPlan.comments"></stringProp>
+ <boolProp name="TestPlan.functional_mode">false</boolProp>
+ <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
+ <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
+ <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+ <collectionProp name="Arguments.arguments"/>
+ </elementProp>
+ <stringProp name="TestPlan.user_define_classpath"></stringProp>
+ </TestPlan>
+ <hashTree>
+ <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="ThreadGroup" enabled="true">
+ <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
+ <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="ThreadGroup" enabled="true">
+ <boolProp name="LoopController.continue_forever">false</boolProp>
+ <intProp name="LoopController.loops">{{ loops }}</intProp>
+ </elementProp>
+ <stringProp name="ThreadGroup.num_threads">{{ num_threads }}</stringProp>
+ <stringProp name="ThreadGroup.ramp_time">{{ ramp_time }}</stringProp>
+ <longProp name="ThreadGroup.start_time">1385457190000</longProp>
+ <longProp name="ThreadGroup.end_time">1385457190000</longProp>
+ <boolProp name="ThreadGroup.scheduler">true</boolProp>
+ <stringProp name="ThreadGroup.duration">60</stringProp>
+ <stringProp name="ThreadGroup.delay"/>
+ <boolProp name="ThreadGroup.delayedStart">true</boolProp>
+ </ThreadGroup>
+ <hashTree>
+ {%- for u in url_list %}
+ <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="{{ url_names[loop.index0] }}" enabled="true">
+ <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="{{ url_names[loop.index0] }}" enabled="true">
+ <collectionProp name="Arguments.arguments"/>
+ </elementProp>
+ <stringProp name="HTTPSampler.domain">{{ u['domain'] }}</stringProp>
+ <stringProp name="HTTPSampler.port">{{ u['port'] }}</stringProp>
+ <stringProp name="HTTPSampler.connect_timeout"/>
+ <stringProp name="HTTPSampler.response_timeout"/>
+ <stringProp name="HTTPSampler.protocol">http</stringProp>
+ <stringProp name="HTTPSampler.contentEncoding"/>
+ <stringProp name="HTTPSampler.path">{{ u['path'] }}</stringProp>
+ <stringProp name="HTTPSampler.method">{{ url_methods[loop.index0] }}</stringProp>
+ <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+ <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+ <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+ <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+ <boolProp name="HTTPSampler.monitor">false</boolProp>
+ <stringProp name="HTTPSampler.embedded_url_re"/>
+ <stringProp name="HTTPSampler.implementation"/>
+ </HTTPSampler>
+ <hashTree/>
+ {%- endfor %}
+
+
+ </hashTree>
+ <ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
+ <boolProp name="ResultCollector.error_logging">false</boolProp>
+ <objProp>
+ <name>saveConfig</name>
+ <value class="SampleSaveConfiguration">
+ <time>true</time>
+ <latency>true</latency>
+ <timestamp>true</timestamp>
+ <success>true</success>
+ <label>true</label>
+ <code>true</code>
+ <message>true</message>
+ <threadName>true</threadName>
+ <dataType>true</dataType>
+ <encoding>false</encoding>
+ <assertions>true</assertions>
+ <subresults>true</subresults>
+ <responseData>false</responseData>
+ <samplerData>false</samplerData>
+ <xml>false</xml>
+ <fieldNames>true</fieldNames>
+ <responseHeaders>false</responseHeaders>
+ <requestHeaders>false</requestHeaders>
+ <responseDataOnError>false</responseDataOnError>
+ <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
+ <assertionsResultsToSave>0</assertionsResultsToSave>
+ <bytes>true</bytes>
+ <sentBytes>true</sentBytes>
+ <threadCounts>true</threadCounts>
+ <idleTime>true</idleTime>
+ <connectTime>true</connectTime>
+ </value>
+ </objProp>
+ <stringProp name="filename">/jmeter/bin/results2</stringProp>
+ </ResultCollector>
+ <hashTree/>
+
+
+ <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
+ <boolProp name="ResultCollector.error_logging">false</boolProp>
+ <objProp>
+ <name>saveConfig</name>
+ <value class="SampleSaveConfiguration">
+ <time>true</time>
+ <latency>true</latency>
+ <timestamp>true</timestamp>
+ <success>true</success>
+ <label>true</label>
+ <code>true</code>
+ <message>true</message>
+ <threadName>true</threadName>
+ <dataType>true</dataType>
+ <encoding>false</encoding>
+ <assertions>true</assertions>
+ <subresults>true</subresults>
+ <responseData>false</responseData>
+ <samplerData>false</samplerData>
+ <xml>false</xml>
+ <fieldNames>true</fieldNames>
+ <responseHeaders>false</responseHeaders>
+ <requestHeaders>false</requestHeaders>
+ <responseDataOnError>false</responseDataOnError>
+ <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
+ <assertionsResultsToSave>0</assertionsResultsToSave>
+ <bytes>true</bytes>
+ <sentBytes>true</sentBytes>
+ <threadCounts>true</threadCounts>
+ <idleTime>true</idleTime>
+ <connectTime>true</connectTime>
+ </value>
+ </objProp>
+ <stringProp name="filename">/jmeter/bin/results_aggregate</stringProp>
+ </ResultCollector>
+ <hashTree/>
+
+
+ </hashTree>
+ </hashTree>
+</jmeterTestPlan>
diff --git a/clover/tools/jmeter/jmeter-slave/Dockerfile b/clover/tools/jmeter/jmeter-slave/Dockerfile
new file mode 100644
index 0000000..b5ccbcd
--- /dev/null
+++ b/clover/tools/jmeter/jmeter-slave/Dockerfile
@@ -0,0 +1,27 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+FROM java:8
+
+RUN wget http://apache.mirrors.hoobly.com//jmeter/binaries/apache-jmeter-4.0.tgz
+RUN tar -xvzf apache-jmeter-4.0.tgz
+RUN rm apache-jmeter-4.0.tgz
+RUN mv apache-jmeter-4.0 /jmeter
+ENV JMETER_HOME /jmeter
+ENV PATH $JMETER_HOME/bin:$PATH
+RUN mkdir /share
+COPY rmi_keystore.jks $JMETER_HOME/bin
+
+WORKDIR $JMETER_HOME
+# Ports to be exposed from the container for JMeter Master
+RUN mkdir scripts
+
+EXPOSE 1099
+
+WORKDIR /jmeter/bin
+
+CMD ./jmeter-server
diff --git a/clover/tools/jmeter/rmi_keystore.jks b/clover/tools/jmeter/rmi_keystore.jks
new file mode 100644
index 0000000..f503361
--- /dev/null
+++ b/clover/tools/jmeter/rmi_keystore.jks
Binary files differ
diff --git a/clover/tools/jmeter/yaml/manifest.template b/clover/tools/jmeter/yaml/manifest.template
new file mode 100644
index 0000000..01a4595
--- /dev/null
+++ b/clover/tools/jmeter/yaml/manifest.template
@@ -0,0 +1,45 @@
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: {{ deploy_name }}
+ labels:
+ app: {{ deploy_name }}
+spec:
+{%- if replica_count %}
+ replicas: 3{% endif %}
+ template:
+ metadata:
+ labels:
+ app: {{ deploy_name }}
+ spec:
+ containers:
+ - name: {{ deploy_name }}
+ image: {{ image_path }}/{{ image_name }}:{{ image_tag }}
+ ports:
+{%- if grpc_port %}
+ - containerPort: {{ grpc_port }}{% endif %}
+ - containerPort: {{ rmi_port }}
+ - containerPort: {{ http_port }}
+ - containerPort: {{ ssl_port }}
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ deploy_name }}
+ labels:
+ app: {{ deploy_name }}
+spec:
+ ports:
+{%- if grpc_port %}
+ - port: {{ grpc_port }}
+ name: grpc{% endif %}
+ - port: {{ rmi_port }}
+ name: rmi
+ - port: {{ http_port }}
+ name: http
+ - port: {{ ssl_port }}
+ name: https
+ selector:
+ app: {{ deploy_name }}
+---
diff --git a/clover/tools/jmeter/yaml/render_master.py b/clover/tools/jmeter/yaml/render_master.py
new file mode 100644
index 0000000..884ee81
--- /dev/null
+++ b/clover/tools/jmeter/yaml/render_master.py
@@ -0,0 +1,67 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+import argparse
+
+from jinja2 import Template
+
+
+def render_yaml(args):
+ template_file = 'manifest.template'
+ out_file = args['deploy_name'] + '.yaml'
+
+ try:
+ with open(template_file) as f:
+ tmpl = Template(f.read())
+ output = tmpl.render(
+ image_path=args['image_path'],
+ image_name=args['image_name'],
+ image_tag=args['image_tag'],
+ deploy_name=args['deploy_name'],
+ grpc_port=args['grpc_port'],
+ ssl_port=args['ssl_port'],
+ rmi_port=args['rmi_port'],
+ http_port=args['http_port']
+ )
+ with open(out_file, "wb") as fh:
+ fh.write(output)
+ return "Generated manifest for {}".format(args['deploy_name'])
+ except Exception as e:
+ print(e)
+ return "Unable to generate manifest for {}".format(
+ args['deploy_name'])
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--image_name', default='clover-jmeter-master',
+ help='The image name to use')
+ parser.add_argument(
+ '--image_path', default='localhost:5000',
+ help='The path to the image to use')
+ parser.add_argument(
+ '--image_tag', default='latest',
+ help='The image tag to use')
+ parser.add_argument(
+ '--deploy_name', default='clover-jmeter-master',
+ help='The k8s deploy name to use')
+ parser.add_argument(
+ '--rmi_port', default='1099',
+ help='The master-slave remote method invocation port')
+ parser.add_argument(
+ '--http_port', default='80',
+ help='HTTP data-plane traffic')
+ parser.add_argument(
+ '--grpc_port', default='50054',
+ help='The GRPC server port for management')
+ parser.add_argument(
+ '--ssl_port', default='443',
+ help='HTTPS data-plane traffic')
+
+ args = parser.parse_args()
+ print(render_yaml(vars(args)))
diff --git a/clover/tools/jmeter/yaml/render_slave.py b/clover/tools/jmeter/yaml/render_slave.py
new file mode 100644
index 0000000..cd9fd91
--- /dev/null
+++ b/clover/tools/jmeter/yaml/render_slave.py
@@ -0,0 +1,67 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+import argparse
+
+from jinja2 import Template
+
+
+def render_yaml(args):
+ template_file = 'manifest.template'
+ out_file = args['deploy_name'] + '.yaml'
+
+ try:
+ with open(template_file) as f:
+ tmpl = Template(f.read())
+ output = tmpl.render(
+ image_path=args['image_path'],
+ image_name=args['image_name'],
+ image_tag=args['image_tag'],
+ deploy_name=args['deploy_name'],
+ replica_count=args['replica_count'],
+ ssl_port=args['ssl_port'],
+ rmi_port=args['rmi_port'],
+ http_port=args['http_port']
+ )
+ with open(out_file, "wb") as fh:
+ fh.write(output)
+ return "Generated manifest for {}".format(args['deploy_name'])
+ except Exception as e:
+ print(e)
+ return "Unable to generate manifest for {}".format(
+ args['deploy_name'])
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--image_name', default='clover-jmeter-slave',
+ help='The image name to use')
+ parser.add_argument(
+ '--image_path', default='localhost:5000',
+ help='The path to the image to use')
+ parser.add_argument(
+ '--image_tag', default='latest',
+ help='The image tag to use')
+ parser.add_argument(
+ '--deploy_name', default='clover-jmeter-slave',
+ help='The k8s deploy name to use')
+ parser.add_argument(
+ '--rmi_port', default='1099',
+ help='The master-slave remote method invocation port')
+ parser.add_argument(
+ '--http_port', default='80',
+ help='HTTP data-plane traffic')
+ parser.add_argument(
+ '--replica_count', default='3',
+ help='Number of replicas in slave deployment')
+ parser.add_argument(
+ '--ssl_port', default='443',
+ help='HTTPS data-plane traffic')
+
+ args = parser.parse_args()
+ print(render_yaml(vars(args)))
diff --git a/clover/tools/yaml/cassandra.yaml b/clover/tools/yaml/cassandra.yaml
new file mode 100644
index 0000000..83ed20f
--- /dev/null
+++ b/clover/tools/yaml/cassandra.yaml
@@ -0,0 +1,97 @@
+#
+#
+# The addition below can be executed after the invocation of
+# the original service_delivery_controller_opnfv.yaml
+# by invoking:
+# % kubectl create -f <(istioctl kube-inject --debug -f cassandra.yaml)
+#
+#
+#
+# Cassandra Statefulset
+#
+# Assumption:
+# - The Cassandra Statefulset right now assumes that
+# the service is deployed in the 'default' namespace
+# therefore the environment variable CASSANDRA_SEEDS is set to
+# "cassandra-0.cassandra.default.svc.cluster.local" below.
+# if you want to use your own name space other than 'default'
+# the value becomes:
+# "cassandra-0.cassandra.<NAMESPACE_NAME>.svc.cluster.local"
+# note that this value rely on a functioning DNS on kubernetes to resolve the IP
+#
+# This static method can be made dynamic by using Helm
+# or bash script to replace the namespace on the fly.
+#
+# - Cassandra launch only 1 replica in the cluster
+# to dynamically scale up or down, you can use:
+# kubectl edit statefulset cassandra
+# and edit the value of the replica key
+#
+#
+#
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app: cassandra
+ name: cassandra
+spec:
+ clusterIP: None
+ ports:
+ - port: 9042
+ selector:
+ app: cassandra
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: cassandra
+ labels:
+ app: cassandra
+spec:
+ serviceName: cassandra
+ replicas: 1
+ selector:
+ matchLabels:
+ app: cassandra
+ template:
+ metadata:
+ labels:
+ app: cassandra
+ spec:
+ terminationGracePeriodSeconds: 1800
+ containers:
+ - name: cassandra
+ image: cassandra:3
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 7000
+ name: intra-node
+ - containerPort: 7001
+ name: tls-intra-node
+ - containerPort: 7199
+ name: jmx
+ - containerPort: 9042
+ name: cql
+ resources:
+ limits:
+ cpu: "500m"
+ memory: 1Gi
+ requests:
+ cpu: "500m"
+ memory: 1Gi
+ env:
+ - name: MAX_HEAP_SIZE
+ value: 512M
+ - name: HEAP_NEWSIZE
+ value: 100M
+ - name: CASSANDRA_SEEDS
+ value: "cassandra-0.cassandra.default.svc.cluster.local"
+ - name: CASSANDRA_CLUSTER_NAME
+ value: "MyCassandraDemo"
+ - name: CASSANDRA_DC
+ value: "DC1-K8Demo"
+ - name: CASSANDRA_RACK
+ value: "Rack1-K8Demo"
+
diff --git a/edge/sample/Network.png b/edge/sample/Network.png
new file mode 100644
index 0000000..1897335
--- /dev/null
+++ b/edge/sample/Network.png
Binary files differ
diff --git a/edge/sample/README.md b/edge/sample/README.md
new file mode 100644
index 0000000..3d5d2f6
--- /dev/null
+++ b/edge/sample/README.md
@@ -0,0 +1,54 @@
+# Edge Cloud Native Cluster
+
+This project revolves around the implementation of a kubernetes-based edge cluster supporting cloud-native framework on which exemplar micro-serives can be deployed with ease. The current implementation of this edge cluster uses six Raspberry Pi boards (one master - five slaves) and the goal is to demonstrate a live video streaming micro-service with end-to-end video streaming from this cluster to the Google Kubernetes Engine. The system would be integrated with Clover in the future. The key idea would be to inject Clover proxy to the existing live video streaming app and test the working of the service mesh functions as well as perform tests to check the continuous deployment (CD) trait as advertised in Clover.
+
+Current Status: Raspberry Pi based kubernetes edge cluster has been built and tested.
+
+## Setting up the Raspberry Pi cluster
+
+These instructions will get you a copy of the cluster up and running on your local system for development and testing purposes. Note that the scripts have been tested on Raspberry Pi 3 model B but should work on other versions also.
+
+### Prerequisites
+
+- At least two Raspberry Pi 3 are required.
+- Your computer and the RasPis should be on the same network. This can be achieved by connecting all the RasPis to the same router (via ethernet) with which your computer is connected (use a switch, if needed).
+![](Network.png)
+- Class-10 32GB microSD cards are preferred.
+- Each RasPi should preferably be powered with the standard 5V, 2.5A supply.
+- Ansible 2.2 or higher on the host computer.
+- Internet access.
+
+### Initial Steps
+
+1. Download the official [Raspbian Stretch Lite](https://www.raspberrypi.org/downloads/raspbian/) OS and [flash](https://etcher.io) on all the microSD cards.
+2. Enable ssh before OS installation for headless mode. This can be achieved by simply making an empty file by the name "ssh" (`$touch ssh`) in the boot partition of the flashed microSD card.
+3. Preferably, change the hostname of all the RasPis to something like master-kube, slave1-kube, etc. for ease of distinguishability. To do this, simply edit "raspberrypi" in */etc/hostname* and */etc/hosts* files and reboot.
+4. Enable passwordless-ssh access to each Raspberry Pi (needed for Ansible). Follow the instructions [here](https://www.raspberrypi.org/documentation/remote-access/ssh/passwordless.md).
+5. All RasPis should have static IP addresses to ensure that the kubernetes cluster does not break. A simple way to do this is to make entries for each RasPi in the address revervation table (under DHCP) of your router.
+
+### Ansible Playbook
+
+The rest of the steps from checking dependencies and system configurations to docker and kubernetes installation to cluster formation are automated using ansible playbook.
+
+1. Clone the project to your local machine which has ansible and is on the same network as the RasPis.
+`git clone https://github.com/opnfv/clover.git`
+
+2. Navigate to the sample directory present inside the edge directory.
+`cd clover/edge/sample/`
+
+3. Edit the *hosts* file. Remove all the IP addresses present in the file by default and simply add the IP address of your master RasPi under the *master* group and the addresses of your slave RasPis under the *slaves* group.
+
+4. Check if Ansible is able to reach all the RasPis by running the following ping command-
+`$ansible kube-cluster -m ping`
+
+5. Finally, run the *clusterForm* yaml playbook-
+`$ansible-playbook clusterForm.yml`
+
+Assuming no errors occurred in the previous step, you can now ssh into your master RasPi and check if the cluster is up and running-
+```
+kubectl get nodes
+kubectl get pods --all-namespaces
+```
+To teardown the cluster, simply run the *clusterTear* yaml-
+`$ansible-playbook clusterTear.yml`
+Run *clusterForm* yaml to again build the cluster.
diff --git a/edge/sample/ansible.cfg b/edge/sample/ansible.cfg
new file mode 100644
index 0000000..d1cc702
--- /dev/null
+++ b/edge/sample/ansible.cfg
@@ -0,0 +1,475 @@
+# config file for ansible -- https://ansible.com/
+# ===============================================
+
+# nearly all parameters can be overridden in ansible-playbook
+# or with command line flags. ansible will read ANSIBLE_CONFIG,
+# ansible.cfg in the current working directory, .ansible.cfg in
+# the home directory or /etc/ansible/ansible.cfg, whichever it
+# finds first
+
+[defaults]
+
+# some basic default values...
+
+inventory = hosts
+#library = /usr/share/my_modules/
+#module_utils = /usr/share/my_module_utils/
+#remote_tmp = ~/.ansible/tmp
+#local_tmp = ~/.ansible/tmp
+#plugin_filters_cfg = /etc/ansible/plugin_filters.yml
+#forks = 5
+#poll_interval = 15
+#sudo_user = root
+#ask_sudo_pass = True
+#ask_pass = True
+#transport = smart
+#remote_port = 22
+#module_lang = C
+#module_set_locale = False
+
+# plays will gather facts by default, which contain information about
+# the remote system.
+#
+# smart - gather by default, but don't regather if already gathered
+# implicit - gather by default, turn off with gather_facts: False
+# explicit - do not gather by default, must say gather_facts: True
+#gathering = implicit
+
+# This only affects the gathering done by a play's gather_facts directive,
+# by default gathering retrieves all facts subsets
+# all - gather all subsets
+# network - gather min and network facts
+# hardware - gather hardware facts (longest facts to retrieve)
+# virtual - gather min and virtual facts
+# facter - import facts from facter
+# ohai - import facts from ohai
+# You can combine them using comma (ex: network,virtual)
+# You can negate them using ! (ex: !hardware,!facter,!ohai)
+# A minimal set of facts is always gathered.
+#gather_subset = all
+
+# some hardware related facts are collected
+# with a maximum timeout of 10 seconds. This
+# option lets you increase or decrease that
+# timeout to something more suitable for the
+# environment.
+# gather_timeout = 10
+
+# additional paths to search for roles in, colon separated
+#roles_path = /etc/ansible/roles
+
+# uncomment this to disable SSH key host checking
+#host_key_checking = False
+
+# change the default callback, you can only have one 'stdout' type enabled at a time.
+#stdout_callback = skippy
+
+
+## Ansible ships with some plugins that require whitelisting,
+## this is done to avoid running all of a type by default.
+## These setting lists those that you want enabled for your system.
+## Custom plugins should not need this unless plugin author specifies it.
+
+# enable callback plugins, they can output to stdout but cannot be 'stdout' type.
+#callback_whitelist = timer, mail
+
+# Determine whether includes in tasks and handlers are "static" by
+# default. As of 2.0, includes are dynamic by default. Setting these
+# values to True will make includes behave more like they did in the
+# 1.x versions.
+#task_includes_static = False
+#handler_includes_static = False
+
+# Controls if a missing handler for a notification event is an error or a warning
+#error_on_missing_handler = True
+
+# change this for alternative sudo implementations
+#sudo_exe = sudo
+
+# What flags to pass to sudo
+# WARNING: leaving out the defaults might create unexpected behaviours
+#sudo_flags = -H -S -n
+
+# SSH timeout
+#timeout = 10
+
+# default user to use for playbooks if user is not specified
+# (/usr/bin/ansible will use current user as default)
+#remote_user = root
+
+# logging is off by default unless this path is defined
+# if so defined, consider logrotate
+#log_path = /var/log/ansible.log
+
+# default module name for /usr/bin/ansible
+#module_name = command
+
+# use this shell for commands executed under sudo
+# you may need to change this to bin/bash in rare instances
+# if sudo is constrained
+#executable = /bin/sh
+
+# if inventory variables overlap, does the higher precedence one win
+# or are hash values merged together? The default is 'replace' but
+# this can also be set to 'merge'.
+#hash_behaviour = replace
+
+# by default, variables from roles will be visible in the global variable
+# scope. To prevent this, the following option can be enabled, and only
+# tasks and handlers within the role will see the variables there
+#private_role_vars = yes
+
+# list any Jinja2 extensions to enable here:
+#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
+
+# if set, always use this private key file for authentication, same as
+# if passing --private-key to ansible or ansible-playbook
+#private_key_file = /path/to/file
+
+# If set, configures the path to the Vault password file as an alternative to
+# specifying --vault-password-file on the command line.
+#vault_password_file = /path/to/vault_password_file
+
+# format of string {{ ansible_managed }} available within Jinja2
+# templates indicates to users editing templates files will be replaced.
+# replacing {file}, {host} and {uid} and strftime codes with proper values.
+#ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
+# {file}, {host}, {uid}, and the timestamp can all interfere with idempotence
+# in some situations so the default is a static string:
+#ansible_managed = Ansible managed
+
+# by default, ansible-playbook will display "Skipping [host]" if it determines a task
+# should not be run on a host. Set this to "False" if you don't want to see these "Skipping"
+# messages. NOTE: the task header will still be shown regardless of whether or not the
+# task is skipped.
+#display_skipped_hosts = True
+
+# by default, if a task in a playbook does not include a name: field then
+# ansible-playbook will construct a header that includes the task's action but
+# not the task's args. This is a security feature because ansible cannot know
+# if the *module* considers an argument to be no_log at the time that the
+# header is printed. If your environment doesn't have a problem securing
+# stdout from ansible-playbook (or you have manually specified no_log in your
+# playbook on all of the tasks where you have secret information) then you can
+# safely set this to True to get more informative messages.
+#display_args_to_stdout = False
+
+# by default (as of 1.3), Ansible will raise errors when attempting to dereference
+# Jinja2 variables that are not set in templates or action lines. Uncomment this line
+# to revert the behavior to pre-1.3.
+#error_on_undefined_vars = False
+
+# by default (as of 1.6), Ansible may display warnings based on the configuration of the
+# system running ansible itself. This may include warnings about 3rd party packages or
+# other conditions that should be resolved if possible.
+# to disable these warnings, set the following value to False:
+#system_warnings = True
+
+# by default (as of 1.4), Ansible may display deprecation warnings for language
+# features that should no longer be used and will be removed in future versions.
+# to disable these warnings, set the following value to False:
+#deprecation_warnings = True
+
+# (as of 1.8), Ansible can optionally warn when usage of the shell and
+# command module appear to be simplified by using a default Ansible module
+# instead. These warnings can be silenced by adjusting the following
+# setting or adding warn=yes or warn=no to the end of the command line
+# parameter string. This will for example suggest using the git module
+# instead of shelling out to the git command.
+# command_warnings = False
+
+
+# set plugin path directories here, separate with colons
+#action_plugins = /usr/share/ansible/plugins/action
+#cache_plugins = /usr/share/ansible/plugins/cache
+#callback_plugins = /usr/share/ansible/plugins/callback
+#connection_plugins = /usr/share/ansible/plugins/connection
+#lookup_plugins = /usr/share/ansible/plugins/lookup
+#inventory_plugins = /usr/share/ansible/plugins/inventory
+#vars_plugins = /usr/share/ansible/plugins/vars
+#filter_plugins = /usr/share/ansible/plugins/filter
+#test_plugins = /usr/share/ansible/plugins/test
+#terminal_plugins = /usr/share/ansible/plugins/terminal
+#strategy_plugins = /usr/share/ansible/plugins/strategy
+
+
+# by default, ansible will use the 'linear' strategy but you may want to try
+# another one
+#strategy = free
+
+# by default callbacks are not loaded for /bin/ansible, enable this if you
+# want, for example, a notification or logging callback to also apply to
+# /bin/ansible runs
+#bin_ansible_callbacks = False
+
+
+# don't like cows? that's unfortunate.
+# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
+#nocows = 1
+
+# set which cowsay stencil you'd like to use by default. When set to 'random',
+# a random stencil will be selected for each task. The selection will be filtered
+# against the `cow_whitelist` option below.
+#cow_selection = default
+#cow_selection = random
+
+# when using the 'random' option for cowsay, stencils will be restricted to this list.
+# it should be formatted as a comma-separated list with no spaces between names.
+# NOTE: line continuations here are for formatting purposes only, as the INI parser
+# in python does not support them.
+#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,\
+# hellokitty,kitty,luke-koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,\
+# stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www
+
+# don't like colors either?
+# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
+#nocolor = 1
+
+# if set to a persistent type (not 'memory', for example 'redis') fact values
+# from previous runs in Ansible will be stored. This may be useful when
+# wanting to use, for example, IP information from one group of servers
+# without having to talk to them in the same playbook run to get their
+# current IP information.
+#fact_caching = memory
+
+
+# retry files
+# When a playbook fails by default a .retry file will be created in ~/
+# You can disable this feature by setting retry_files_enabled to False
+# and you can change the location of the files by setting retry_files_save_path
+
+#retry_files_enabled = False
+#retry_files_save_path = ~/.ansible-retry
+
+# squash actions
+# Ansible can optimise actions that call modules with list parameters
+# when looping. Instead of calling the module once per with_ item, the
+# module is called once with all items at once. Currently this only works
+# under limited circumstances, and only with parameters named 'name'.
+#squash_actions = apk,apt,dnf,homebrew,pacman,pkgng,yum,zypper
+
+# prevents logging of task data, off by default
+#no_log = False
+
+# prevents logging of tasks, but only on the targets, data is still logged on the master/controller
+#no_target_syslog = False
+
+# controls whether Ansible will raise an error or warning if a task has no
+# choice but to create world readable temporary files to execute a module on
+# the remote machine. This option is False by default for security. Users may
+# turn this on to have behaviour more like Ansible prior to 2.1.x. See
+# https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user
+# for more secure ways to fix this than enabling this option.
+#allow_world_readable_tmpfiles = False
+
+# controls the compression level of variables sent to
+# worker processes. At the default of 0, no compression
+# is used. This value must be an integer from 0 to 9.
+#var_compression_level = 9
+
+# controls what compression method is used for new-style ansible modules when
+# they are sent to the remote system. The compression types depend on having
+# support compiled into both the controller's python and the client's python.
+# The names should match with the python Zipfile compression types:
+# * ZIP_STORED (no compression. available everywhere)
+# * ZIP_DEFLATED (uses zlib, the default)
+# These values may be set per host via the ansible_module_compression inventory
+# variable
+#module_compression = 'ZIP_DEFLATED'
+
+# This controls the cutoff point (in bytes) on --diff for files
+# set to 0 for unlimited (RAM may suffer!).
+#max_diff_size = 1048576
+
+# This controls how ansible handles multiple --tags and --skip-tags arguments
+# on the CLI. If this is True then multiple arguments are merged together. If
+# it is False, then the last specified argument is used and the others are ignored.
+# This option will be removed in 2.8.
+#merge_multiple_cli_flags = True
+
+# Controls showing custom stats at the end, off by default
+#show_custom_stats = True
+
+# Controls which files to ignore when using a directory as inventory with
+# possibly multiple sources (both static and dynamic)
+#inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo
+
+# This family of modules use an alternative execution path optimized for network appliances
+# only update this setting if you know how this works, otherwise it can break module execution
+#network_group_modules=eos, nxos, ios, iosxr, junos, vyos
+
+# When enabled, this option allows lookups (via variables like {{lookup('foo')}} or when used as
+# a loop with `with_foo`) to return data that is not marked "unsafe". This means the data may contain
+# jinja2 templating language which will be run through the templating engine.
+# ENABLING THIS COULD BE A SECURITY RISK
+#allow_unsafe_lookups = False
+
+# set default errors for all plays
+#any_errors_fatal = False
+
+[inventory]
+# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini'
+#enable_plugins = host_list, virtualbox, yaml, constructed
+
+# ignore these extensions when parsing a directory as inventory source
+#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry
+
+# ignore files matching these patterns when parsing a directory as inventory source
+#ignore_patterns=
+
+# If 'true' unparsed inventory sources become fatal errors, they are warnings otherwise.
+#unparsed_is_failed=False
+
+[privilege_escalation]
+#become=True
+#become_method=sudo
+#become_user=root
+#become_ask_pass=False
+
+[paramiko_connection]
+
+# uncomment this line to cause the paramiko connection plugin to not record new host
+# keys encountered. Increases performance on new host additions. Setting works independently of the
+# host key checking setting above.
+#record_host_keys=False
+
+# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
+# line to disable this behaviour.
+#pty=False
+
+# paramiko will default to looking for SSH keys initially when trying to
+# authenticate to remote devices. This is a problem for some network devices
+# that close the connection after a key failure. Uncomment this line to
+# disable the Paramiko look for keys function
+#look_for_keys = False
+
+# When using persistent connections with Paramiko, the connection runs in a
+# background process. If the host doesn't already have a valid SSH key, by
+# default Ansible will prompt to add the host key. This will cause connections
+# running in background processes to fail. Uncomment this line to have
+# Paramiko automatically add host keys.
+#host_key_auto_add = True
+
+[ssh_connection]
+
+# ssh arguments to use
+# Leaving off ControlPersist will result in poor performance, so use
+# paramiko on older platforms rather than removing it, -C controls compression use
+#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s
+
+# The base directory for the ControlPath sockets.
+# This is the "%(directory)s" in the control_path option
+#
+# Example:
+# control_path_dir = /tmp/.ansible/cp
+#control_path_dir = ~/.ansible/cp
+
+# The path to use for the ControlPath sockets. This defaults to a hashed string of the hostname,
+# port and username (empty string in the config). The hash mitigates a common problem users
+# found with long hostames and the conventional %(directory)s/ansible-ssh-%%h-%%p-%%r format.
+# In those cases, a "too long for Unix domain socket" ssh error would occur.
+#
+# Example:
+# control_path = %(directory)s/%%h-%%r
+#control_path =
+
+# Enabling pipelining reduces the number of SSH operations required to
+# execute a module on the remote server. This can result in a significant
+# performance improvement when enabled, however when using "sudo:" you must
+# first disable 'requiretty' in /etc/sudoers
+#
+# By default, this option is disabled to preserve compatibility with
+# sudoers configurations that have requiretty (the default on many distros).
+#
+#pipelining = False
+
+# Control the mechanism for transferring files (old)
+# * smart = try sftp and then try scp [default]
+# * True = use scp only
+# * False = use sftp only
+#scp_if_ssh = smart
+
+# Control the mechanism for transferring files (new)
+# If set, this will override the scp_if_ssh option
+# * sftp = use sftp to transfer files
+# * scp = use scp to transfer files
+# * piped = use 'dd' over SSH to transfer files
+# * smart = try sftp, scp, and piped, in that order [default]
+#transfer_method = smart
+
+# if False, sftp will not use batch mode to transfer files. This may cause some
+# types of file transfer failures impossible to catch however, and should
+# only be disabled if your sftp version has problems with batch mode
+#sftp_batch_mode = False
+
+# The -tt argument is passed to ssh when pipelining is not enabled because sudo
+# requires a tty by default.
+#use_tty = True
+
+[persistent_connection]
+
+# Configures the persistent connection timeout value in seconds. This value is
+# how long the persistent connection will remain idle before it is destroyed.
+# If the connection doesn't receive a request before the timeout value
+# expires, the connection is shutdown. The default value is 30 seconds.
+#connect_timeout = 30
+
+# Configures the persistent connection retry timeout. This value configures the
+# the retry timeout that ansible-connection will wait to connect
+# to the local domain socket. This value must be larger than the
+# ssh timeout (timeout) and less than persistent connection idle timeout (connect_timeout).
+# The default value is 15 seconds.
+#connect_retry_timeout = 15
+
+# The command timeout value defines the amount of time to wait for a command
+# or RPC call before timing out. The value for the command timeout must
+# be less than the value of the persistent connection idle timeout (connect_timeout)
+# The default value is 10 second.
+#command_timeout = 10
+
+[accelerate]
+#accelerate_port = 5099
+#accelerate_timeout = 30
+#accelerate_connect_timeout = 5.0
+
+# The daemon timeout is measured in minutes. This time is measured
+# from the last activity to the accelerate daemon.
+#accelerate_daemon_timeout = 30
+
+# If set to yes, accelerate_multi_key will allow multiple
+# private keys to be uploaded to it, though each user must
+# have access to the system via SSH to add a new key. The default
+# is "no".
+#accelerate_multi_key = yes
+
+[selinux]
+# file systems that require special treatment when dealing with security context
+# the default behaviour that copies the existing context or uses the user default
+# needs to be changed to use the file system dependent context.
+#special_context_filesystems=nfs,vboxsf,fuse,ramfs,9p
+
+# Set this to yes to allow libvirt_lxc connections to work without SELinux.
+#libvirt_lxc_noseclabel = yes
+
+[colors]
+#highlight = white
+#verbose = blue
+#warn = bright purple
+#error = red
+#debug = dark gray
+#deprecate = purple
+#skip = cyan
+#unreachable = red
+#ok = green
+#changed = yellow
+#diff_add = green
+#diff_remove = red
+#diff_lines = cyan
+
+
+[diff]
+# Always print diff when running ( same as always running with -D/--diff )
+# always = no
+
+# Set how many context lines to show in diff
+# context = 3
diff --git a/edge/sample/clusterForm.yml b/edge/sample/clusterForm.yml
new file mode 100644
index 0000000..81752aa
--- /dev/null
+++ b/edge/sample/clusterForm.yml
@@ -0,0 +1,15 @@
+---
+- hosts: all
+ roles:
+ - clusterForm_common
+ become: true
+
+- hosts: master
+ roles:
+ - clusterForm_master
+ become: true
+
+- hosts: slaves
+ roles:
+ - clusterForm_slave(s)
+ become: true
diff --git a/edge/sample/clusterTear.yml b/edge/sample/clusterTear.yml
new file mode 100644
index 0000000..0c43a1c
--- /dev/null
+++ b/edge/sample/clusterTear.yml
@@ -0,0 +1,5 @@
+---
+- hosts: all
+ roles:
+ - clusterTear_common
+ become: true
diff --git a/edge/sample/hosts b/edge/sample/hosts
new file mode 100644
index 0000000..8a4d302
--- /dev/null
+++ b/edge/sample/hosts
@@ -0,0 +1,16 @@
+[master]
+192.168.1.107
+
+[slaves]
+192.168.1.103
+192.168.1.108
+192.168.1.109
+192.168.1.110
+192.168.1.111
+
+[kube-cluster:children]
+master
+slaves
+
+[kube-cluster:vars]
+ansible_ssh_user=pi
diff --git a/edge/sample/roles/clusterForm_common/tasks/main.yml b/edge/sample/roles/clusterForm_common/tasks/main.yml
new file mode 100644
index 0000000..a690d28
--- /dev/null
+++ b/edge/sample/roles/clusterForm_common/tasks/main.yml
@@ -0,0 +1,127 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+- name: Checking dependencies for Docker
+ shell: docker --version
+ register: doc_ver
+ ignore_errors: true
+ failed_when: false
+
+- name: Removing unsupported version of Docker (if any)
+ apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - 'docker'
+ - 'docker-engine'
+ - 'docker-ce'
+ - 'docker.io'
+ when: doc_ver.stdout.find('18.04.0-ce') == -1
+
+- name: Adding GPG key for Docker
+ apt_key:
+ url: https://download.docker.com/linux/debian/gpg
+ state: present
+ when: doc_ver.stdout.find('18.04.0-ce') == -1
+
+- name: Updating sources.list.d directory
+ shell: echo "deb [arch=armhf] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) edge" | \ tee /etc/apt/sources.list.d/docker.list
+ when: doc_ver.stdout.find('18.04.0-ce') == -1
+
+- name: Installing Docker 18.04.0
+ apt:
+ name: docker-ce=18.04.0~ce~3-0~raspbian
+ update_cache: yes
+ when: doc_ver.stdout.find('18.04.0-ce') == -1
+
+- name: Adding user 'pi' to the 'docker' group
+ shell: usermod pi -aG docker
+ when: doc_ver.stdout.find('18.04.0-ce') == -1
+
+- name: Removing docker from apt sources to prevent upgrade
+ file:
+ path: /etc/apt/sources.list.d/docker.list
+ state: absent
+
+- name: Turning off swap
+ shell: dphys-swapfile swapoff && dphys-swapfile uninstall && update-rc.d dphys-swapfile remove
+
+- name: Checking cgroup dependencies
+ shell: cat /boot/cmdline.txt
+ register: boot
+
+- name: Enabling cpuset cgroup
+ shell: sed -i 's/$/ cgroup_enable=cpuset/' /boot/cmdline.txt
+ args:
+ warn: false
+ when: boot.stdout.find('cgroup_enable=cpuset') == -1
+
+- name: Enabling memory cgroup (1/2)
+ shell: sed -i 's/$/ cgroup_memory=1/' /boot/cmdline.txt
+ args:
+ warn: false
+ when: boot.stdout.find('cgroup_memory=1') == -1
+
+- name: Enabling memory cgroup (2/2)
+ shell: sed -i 's/$/ cgroup_enable=memory/' /boot/cmdline.txt
+ args:
+ warn: false
+ when: boot.stdout.find('cgroup_enable=memory') == -1
+
+- name: Rebooting
+ shell: sleep 2 && reboot
+ async: 1
+ poll: 0
+ ignore_errors: true
+ when: boot.stdout.find('cgroup_enable=cpuset') == -1 or boot.stdout.find('cgroup_memory=1') == -1 or boot.stdout.find('cgroup_enable=memory') == -1
+
+- name: Waiting for host(s) to come online
+ wait_for_connection:
+ delay: 30
+ when: boot.stdout.find('cgroup_enable=cpuset') == -1 or boot.stdout.find('cgroup_memory=1') == -1 or boot.stdout.find('cgroup_enable=memory') == -1
+
+- name: Checking dependencies for Kubernetes
+ shell: kubeadm version
+ register: kube_ver
+ ignore_errors: true
+ failed_when: false
+
+- name: Removing unsupported version of Kubernetes (if any)
+ apt:
+ name: "{{ item }}"
+ state: absent
+ autoremove: yes
+ with_items:
+ - 'kubeadm'
+ - 'kubectl'
+ - 'kubelet'
+ when: kube_ver.stdout.find('v1.10.2') == -1
+
+- name: Adding GPG key for kubernetes
+ apt_key:
+ url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
+ state: present
+ when: kube_ver.stdout.find('v1.10.2') == -1
+
+- name: Updating sources.list.d directory
+ shell: echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
+ when: kube_ver.stdout.find('v1.10.2') == -1
+
+- name: Installing kubeadm, kubectl and kubelet version 1.10.2-00
+ apt:
+ name: "{{ item }}"
+ update_cache: true
+ with_items:
+ - 'kubeadm=1.10.2-00'
+ - 'kubectl=1.10.2-00'
+ - 'kubelet=1.10.2-00'
+ when: kube_ver.stdout.find('v1.10.2') == -1
+
+- name: Removing kubernetes from apt sources to prevent upgrade
+ file:
+ path: /etc/apt/sources.list.d/kubernetes.list
+ state: absent
diff --git a/edge/sample/roles/clusterForm_master/tasks/main.yml b/edge/sample/roles/clusterForm_master/tasks/main.yml
new file mode 100644
index 0000000..9137d7f
--- /dev/null
+++ b/edge/sample/roles/clusterForm_master/tasks/main.yml
@@ -0,0 +1,72 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+- name: Resetting kubeadm on master
+ shell: kubeadm reset
+
+- name: Removing KUBELET_NETWORK_ARGS flag in the kubadm config file
+ shell: sed -i '/KUBELET_NETWORK_ARGS=/d' /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
+ args:
+ warn: false
+
+- name: Removing redundant config files (1/3)
+ file:
+ path: /home/pi/.kube
+ state: absent
+
+- name: Removing redundant config files (2/3)
+ file:
+ path: /home/pi/join.sh
+ state: absent
+
+- name: Removing redundant config files (3/3)
+ file:
+ path: /home/pi/kubelog.txt
+ state: absent
+
+- name: Initializing kubeadm
+ shell: kubeadm init --token-ttl=0 --pod-network-cidr 10.244.0.0/16 --apiserver-advertise-address={{ ansible_default_ipv4.address }} > kubelog.txt
+
+- name: Scraping the join token
+ shell: cat kubelog.txt | grep 'kubeadm join' > join.sh && sed "s/^[ \t]*//" -i join.sh
+ args:
+ warn: false
+ become: false
+
+- name: Fetching the joining script
+ fetch:
+ src: /home/pi/join.sh
+ dest: ./
+ flat: yes
+ become: false
+
+- name: Making a .kube directory in home
+ file:
+ path: /home/pi/.kube
+ state: directory
+ become: false
+
+- name: Copying admin config file to .kube directory
+ copy:
+ src: /etc/kubernetes/admin.conf
+ dest: /home/pi/.kube/config
+ remote_src: yes
+
+- name: Changing user and group ownership of the config file
+ shell: chown $(id -u):$(id -g) /home/pi/.kube/config
+ args:
+ warn: false
+
+- name: Installing Flannel
+ shell: curl -sSL https://rawgit.com/coreos/flannel/v0.10.0/Documentation/kube-flannel.yml | sed "s/amd64/arm/g" | kubectl create -f -
+ args:
+ warn: false
+ become: false
+
+- name: Pause a minute for system containers to spin up
+ pause:
+ minutes: 1
diff --git a/edge/sample/roles/clusterForm_slave(s)/tasks/main.yml b/edge/sample/roles/clusterForm_slave(s)/tasks/main.yml
new file mode 100644
index 0000000..206e86c
--- /dev/null
+++ b/edge/sample/roles/clusterForm_slave(s)/tasks/main.yml
@@ -0,0 +1,12 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+- name: Resetting kubeadm on slaves
+ shell: kubeadm reset
+
+- name: Join the cluster, slaves!
+ script: join.sh
diff --git a/edge/sample/roles/clusterTear_common/tasks/main.yml b/edge/sample/roles/clusterTear_common/tasks/main.yml
new file mode 100644
index 0000000..64b1320
--- /dev/null
+++ b/edge/sample/roles/clusterTear_common/tasks/main.yml
@@ -0,0 +1,21 @@
+# Copyright (c) Authors of Clover
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+- name: Resetting kubeadm
+ shell: kubeadm reset
+
+- name: Deleting the .kube config directory
+ file:
+ path: /home/pi/.kube
+ state: absent
+ when: "'master' in group_names"
+
+- name: Rebooting
+ shell: sleep 2 && reboot
+ async: 1
+ poll: 0
+ ignore_errors: true
diff --git a/samples/scenarios/clearwater_ims/clt-docker/Dockerfile b/samples/scenarios/clearwater_ims/clt-docker/Dockerfile
new file mode 100644
index 0000000..1047521
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/clt-docker/Dockerfile
@@ -0,0 +1,22 @@
+From ubuntu:16.04
+MAINTAINER Salman Shaikh (muhammad.shaikh@huawei.com)
+RUN apt-get update && apt-get -y upgrade
+RUN apt-get install -y build-essential bundler git
+RUN apt-get install -y curl
+#RUN apt-get remove -y ruby
+RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
+RUN curl -L https://get.rvm.io | bash -s stable
+#RUN /bin/bash -c "source /usr/local/rvm/scripts/rvm"
+RUN /bin/bash -c "source /etc/profile.d/rvm.sh \
+ && rvm autolibs enable \
+ && rvm install 1.9.3 \
+ && rvm use 1.9.3"
+#RUN mkdir -p /root/.ssh
+#ADD id_rsa /root/.ssh/id_rsa
+#RUN chmod 700 /root/.ssh/id_rsa
+ADD clearwater-live-test.tgz /opt/
+RUN /bin/bash -c "source /etc/profile.d/rvm.sh \
+ && cd /opt/clearwater-live-test \
+ && bundle install"
+SHELL ["/bin/bash", "-c", "source /etc/profile.d/rvm.sh"]
+#CMD /bin/bash -c "source /etc/profile.d/rvm.sh"
diff --git a/samples/scenarios/clearwater_ims/clt-docker/clearwater-live-test.tgz b/samples/scenarios/clearwater_ims/clt-docker/clearwater-live-test.tgz
new file mode 100644
index 0000000..9bcb417
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/clt-docker/clearwater-live-test.tgz
Binary files differ
diff --git a/samples/scenarios/clearwater_ims/scripts/prov-numbers.sh b/samples/scenarios/clearwater_ims/scripts/prov-numbers.sh
new file mode 100755
index 0000000..a73527a
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/scripts/prov-numbers.sh
@@ -0,0 +1 @@
+kubectl exec -it $(kubectl get pods -l=service=ellis -o jsonpath='{.items[0].metadata.name}') -c ellis -- bash -c "sudo bash -c \"export PATH=/usr/share/clearwater/ellis/env/bin:$PATH ; cd /usr/share/clearwater/ellis/src/metaswitch/ellis/tools/ ; python create_numbers.py --start 6505550000 --count 10\""
diff --git a/samples/scenarios/clearwater_ims/scripts/run-live-test.sh b/samples/scenarios/clearwater_ims/scripts/run-live-test.sh
new file mode 100755
index 0000000..6fb0a50
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/scripts/run-live-test.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+TITLE="System Information for $HOSTNAME"
+RIGHT_NOW=$(date +"%x %r %Z")
+TIME_STAMP="Updated on $RIGHT_NOW by $USER"
+
+BONO_SIP_PROXY_IP=$1
+ELLIS_IP=$2
+BASIC=$3
+
+para1=0
+para2=0
+if [[ -n "$BONO_SIP_PROXY_IP" ]];then
+ para1=1
+else
+ echo "ERROR: Missing External Loadbalancer IP for Bono"
+fi
+if [[ -n "$ELLIS_IP" ]];then
+ para2=1
+else
+ echo "ERROR: Missing External Loadbalancer IP for Ellis"
+fi
+
+if [ "$para1" -eq "0" ];then
+ echo "";echo "USAGE: $0 <BONO_SIP_PROXY_IP> <ELLIS_IP>";echo ""
+ exit
+fi
+if [ "$para2" -eq "0" ];then
+ echo "";echo "USAGE: $0 <BONO_SIP_PROXY_IP> <ELLIS_IP>";echo ""
+ exit
+fi
+
+
+if [[ $para1 == 1 && $para2 == 1 ]];then
+ if [ "$BASIC" == "basic" ];then
+ docker exec -it live-test bash -c "source /etc/profile.d/rvm.sh && cd /opt/clearwater-live-test && rake test[default.svc.cluster.local] PROXY=$BONO_SIP_PROXY_IP ELLIS=$ELLIS_IP SIGNUP_CODE=\"secret\" TESTS=\"Basic Call - Mainline\""
+ else
+ docker exec -it live-test bash -c "source /etc/profile.d/rvm.sh && cd /opt/clearwater-live-test && rake test[default.svc.cluster.local] PROXY=$BONO_SIP_PROXY_IP ELLIS=$ELLIS_IP SIGNUP_CODE=\"secret\""
+ fi
+fi
diff --git a/samples/scenarios/clearwater_ims/yaml/ellis-depl.yaml b/samples/scenarios/clearwater_ims/yaml/ellis-depl.yaml
new file mode 100644
index 0000000..7cbaf1d
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/ellis-depl.yaml
@@ -0,0 +1,40 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: ellis
+ labels:
+ app: ellis
+spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: ellis
+ service: ellis
+ spec:
+ containers:
+ #- image: "localhost:5000/ellis:clearwater/base:latest"
+ - image: "instance-1:5000/clearwater/ellis:latest"
+ imagePullPolicy: Always
+ name: ellis
+ ports:
+ - containerPort: 22
+ - containerPort: 80
+ envFrom:
+ - configMapRef:
+ name: env-vars
+ env:
+ - name: MY_POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ - name: PUBLIC_IP
+ value: <External Load Balancer IP>
+ livenessProbe:
+ tcpSocket:
+ port: 80
+ initialDelaySeconds: 30
+ readinessProbe:
+ tcpSocket:
+ port: 80
+ restartPolicy: Always
diff --git a/samples/scenarios/clearwater_ims/yaml/ellis-svc.yaml b/samples/scenarios/clearwater_ims/yaml/ellis-svc.yaml
new file mode 100644
index 0000000..da65bd0
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/ellis-svc.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: ellis
+ labels:
+ app: ellis
+spec:
+ #clusterIP: None
+ type: "LoadBalancer"
+ loadBalancerIP: <External Load Balancer IP>
+ ports:
+ - name: "http"
+ port: 80
+ selector:
+ app: ellis
+ service: ellis
diff --git a/samples/scenarios/clearwater_ims/yaml/homer-depl.yaml b/samples/scenarios/clearwater_ims/yaml/homer-depl.yaml
new file mode 100644
index 0000000..d753241
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/homer-depl.yaml
@@ -0,0 +1,38 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: homer
+ labels:
+ app: homer
+spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: homer
+ service: homer
+ spec:
+ containers:
+ #- image: "localhost:5000/homer:clearwater/base:latest"
+ - image: "instance-1:5000/clearwater/homer:latest"
+ imagePullPolicy: Always
+ name: homer
+ ports:
+ - containerPort: 22
+ - containerPort: 7888
+ envFrom:
+ - configMapRef:
+ name: env-vars
+ env:
+ - name: MY_POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ livenessProbe:
+ tcpSocket:
+ port: 7888
+ initialDelaySeconds: 30
+ readinessProbe:
+ tcpSocket:
+ port: 7888
+ restartPolicy: Always
diff --git a/samples/scenarios/clearwater_ims/yaml/homer-svc.yaml b/samples/scenarios/clearwater_ims/yaml/homer-svc.yaml
new file mode 100644
index 0000000..4329843
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/homer-svc.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: homer
+ labels:
+ app: homer
+spec:
+ ports:
+ - name: "7888"
+ port: 7888
+ selector:
+ app: homer
+ service: homer
+ clusterIP: None
diff --git a/samples/scenarios/clearwater_ims/yaml/homestead-depl.yaml b/samples/scenarios/clearwater_ims/yaml/homestead-depl.yaml
new file mode 100644
index 0000000..c30bac0
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/homestead-depl.yaml
@@ -0,0 +1,54 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: homestead
+ labels:
+ app: homestead
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ service: homestead
+ template:
+ metadata:
+ labels:
+ app: homestead
+ service: homestead
+ snmp: enabled
+ spec:
+ containers:
+ #- image: "localhost:5000/homestead:clearwater/base:latest"
+ - image: "instance-1:5000/clearwater/homestead:latest"
+ imagePullPolicy: Always
+ name: homestead
+ ports:
+ - containerPort: 22
+ - containerPort: 8888
+ envFrom:
+ - configMapRef:
+ name: env-vars
+ env:
+ - name: MY_POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ livenessProbe:
+ exec:
+ command: ["/bin/bash", "/usr/share/kubernetes/liveness.sh", "8888"]
+ initialDelaySeconds: 60
+ readinessProbe:
+ exec:
+ command: ["/bin/bash", "/usr/share/kubernetes/liveness.sh", "8888"]
+ volumeMounts:
+ - name: homesteadlogs
+ mountPath: /var/log/homestead
+ - image: busybox
+ name: tailer
+ command: [ "tail", "-F", "/var/log/homestead/homestead_current.txt" ]
+ volumeMounts:
+ - name: homesteadlogs
+ mountPath: /var/log/homestead
+ volumes:
+ - name: homesteadlogs
+ emptyDir: {}
+ restartPolicy: Always
diff --git a/samples/scenarios/clearwater_ims/yaml/homestead-prov-depl.yaml b/samples/scenarios/clearwater_ims/yaml/homestead-prov-depl.yaml
new file mode 100644
index 0000000..18b47ea
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/homestead-prov-depl.yaml
@@ -0,0 +1,42 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: homestead-prov
+ labels:
+ app: homestead-prov
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ service: homestead-prov
+ template:
+ metadata:
+ labels:
+ app: homestead-prov
+ service: homestead-prov
+ snmp: enabled
+ spec:
+ containers:
+ #- image: "localhost:5000/homestead-prov:clearwater/base:latest"
+ - image: "instance-1:5000/clearwater/homestead-prov:latest"
+ imagePullPolicy: Always
+ name: homestead-prov
+ ports:
+ - containerPort: 22
+ - containerPort: 8889
+ envFrom:
+ - configMapRef:
+ name: env-vars
+ env:
+ - name: MY_POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ livenessProbe:
+ exec:
+ command: ["/bin/bash", "/usr/share/clearwater/bin/poll_homestead-prov.sh"]
+ initialDelaySeconds: 60
+ readinessProbe:
+ exec:
+ command: ["/bin/bash", "/usr/share/clearwater/bin/poll_homestead-prov.sh"]
+ restartPolicy: Always
diff --git a/samples/scenarios/clearwater_ims/yaml/homestead-prov-svc.yaml b/samples/scenarios/clearwater_ims/yaml/homestead-prov-svc.yaml
new file mode 100644
index 0000000..66b6358
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/homestead-prov-svc.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: homestead-prov
+ labels:
+ app: homestead-prov
+spec:
+ ports:
+ - name: "8889"
+ port: 8889
+ selector:
+ app: homestead-prov
+ service: homestead-prov
+ clusterIP: None
diff --git a/samples/scenarios/clearwater_ims/yaml/homestead-svc.yaml b/samples/scenarios/clearwater_ims/yaml/homestead-svc.yaml
new file mode 100644
index 0000000..99c1942
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/homestead-svc.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: homestead
+ labels:
+ app: homestead
+spec:
+ ports:
+ - name: "8888"
+ port: 8888
+ selector:
+ app: homestead
+ service: homestead
+ clusterIP: None
diff --git a/samples/scenarios/clearwater_ims/yaml/ralf-depl.yaml b/samples/scenarios/clearwater_ims/yaml/ralf-depl.yaml
new file mode 100644
index 0000000..da6df5f
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/ralf-depl.yaml
@@ -0,0 +1,54 @@
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+ name: ralf
+ labels:
+ app: ralf
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ service: ralf
+ template:
+ metadata:
+ labels:
+ app: ralf
+ service: ralf
+ snmp: enabled
+ spec:
+ containers:
+ #- image: "localhost:5000/ralf:clearwater/base:latest"
+ - image: "instance-1:5000/clearwater/ralf:latest"
+ imagePullPolicy: Always
+ name: ralf
+ ports:
+ - containerPort: 22
+ - containerPort: 10888
+ envFrom:
+ - configMapRef:
+ name: env-vars
+ env:
+ - name: MY_POD_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.podIP
+ livenessProbe:
+ tcpSocket:
+ port: 10888
+ initialDelaySeconds: 30
+ readinessProbe:
+ tcpSocket:
+ port: 10888
+ volumeMounts:
+ - name: ralflogs
+ mountPath: /var/log/ralf
+ - image: busybox
+ name: tailer
+ command: [ "tail", "-F", "/var/log/ralf/ralf_current.txt" ]
+ volumeMounts:
+ - name: ralflogs
+ mountPath: /var/log/ralf
+ volumes:
+ - name: ralflogs
+ emptyDir: {}
+ restartPolicy: Always
diff --git a/samples/scenarios/clearwater_ims/yaml/ralf-svc.yaml b/samples/scenarios/clearwater_ims/yaml/ralf-svc.yaml
new file mode 100644
index 0000000..2e72ac0
--- /dev/null
+++ b/samples/scenarios/clearwater_ims/yaml/ralf-svc.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: ralf
+ labels:
+ app: ralf
+spec:
+ ports:
+ - name: "10888"
+ port: 10888
+ selector:
+ app: ralf
+ service: ralf
+ clusterIP: None
diff --git a/samples/scenarios/service_delivery_controller_opnfv.yaml b/samples/scenarios/service_delivery_controller_opnfv.yaml
index ee0adcc..9fee92f 100644
--- a/samples/scenarios/service_delivery_controller_opnfv.yaml
+++ b/samples/scenarios/service_delivery_controller_opnfv.yaml
@@ -358,4 +358,3 @@ spec:
backend:
serviceName: proxy-access-control
servicePort: 9180
----
diff --git a/xci-k8s-setup.sh b/xci-k8s-setup.sh
new file mode 100755
index 0000000..b08be7a
--- /dev/null
+++ b/xci-k8s-setup.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+set -e
+set -x
+
+CLOVER_DIR=`cd ${BASH_SOURCE[0]%/*}/;pwd`
+export CLOVER_DIR
+
+# Set the variable for deploying k8s
+export XCI_FLAVOR=${XCI_FLAVOR:-mini}
+export INSTALLER_TYPE=${INSTALLER_TYPE:-kubespray}
+export DEPLOY_SCENARIO=${DEPLOY_SCENARIO:-k8-flannel-nofeature}
+
+if [[ $(whoami) == "root" ]]; then
+ echo "ERROR: This script should not be run as root!"
+ exit 1
+fi
+
+WORK_DIR=${CLOVER_DIR}/work
+sudo rm -rf $WORK_DIR
+mkdir $WORK_DIR
+
+# If SSH key doesn't exist generate an SSH key in $HOME/.ssh/
+[[ ! -d "$HOME/.ssh/" ]] && mkdir $HOME/.ssh/
+[[ ! -f "$HOME/.ssh/id_rsa" ]] && ssh-keygen -q -t rsa -f ~/.ssh/id_rsa -N ""
+
+sudo apt-get update
+sudo apt-get install git python-pip -y
+
+git clone https://gerrit.opnfv.org/gerrit/releng-xci $WORK_DIR/releng-xci
+
+cd $WORK_DIR/releng-xci/xci
+
+source xci-deploy.sh
+
+MASTER_IP=$(ssh root@$OPNFV_HOST_IP "grep -r server ~/.kube/config | awk '{print \$2}' |awk -F '[:/]' '{print \$4}'")
+echo "----------------------------------------"
+echo "Info: You can login the Kubernetes Cluster master host"
+echo "ssh root@$MASTER_IP"
+echo "----------------------------------------"