diff options
21 files changed, 631 insertions, 0 deletions
@@ -77,3 +77,4 @@ tags !/src/l2fwd/Makefile docs/_build/* +.DS_Store diff --git a/docs/k8s/helm.rst b/docs/k8s/helm.rst new file mode 100644 index 00000000..add15a7b --- /dev/null +++ b/docs/k8s/helm.rst @@ -0,0 +1,137 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. (c) Anuket, Spirent, AT&T, Ixia and others. + +.. Anuket ViNePerf Documentation master file. + +============================================================ +Automated deployment of helm charts with python +============================================================ + +******************** +Directory Structure +******************** +.. code-block:: console + + + ├── charts + │ ├── mynet.yaml + │ ├── pod.yaml + │ ├── proxchart + │ │ ├── Chart.yaml + │ │ ├── templates + │ │ │ ├── daemonset.yaml + │ │ │ ├── deployment.yaml + │ │ │ └── service.yaml + │ │ └── values.yaml + │ ├── testpmdchart + │ │ ├── Chart.yaml + │ │ ├── templates + │ │ │ ├── daemonset.yaml + │ │ │ ├── deployment.yaml + │ │ │ └── service.yaml + │ │ └── values.yaml + │ └── trexchart + │ ├── Chart.yaml + │ ├── templates + │ │ ├── daemonset.yaml + │ │ ├── deployment.yaml + │ │ └── service.yaml + │ └── values.yaml + └── pyscript + ├── Pipfile + ├── Pipfile.lock + └── main.py + + +*************** +Using the tool +*************** + +Charts folder contains three different charts - ``proxchart, testpmd chart and trex chart``. Any of the one can be used for testing. +Pyscript folder contains ``main.py`` - the main python script that depploys the helm chart of user's choice and returns important configuration of the charts, such as PodIP, Cluster IP, Interface IPs etc. + +************************ +Important Configurations +************************ + +In order to run the python script, we need to install Pipenv to create virtual environment. + +Steps: + +1. Install + +.. code-block:: console + + pip install --user pipenv + +2. To activate the environment + +.. code-block:: console + + pipenv shell + +The last command will automatically read Pipfile.lock and will create an virtual environment. + +Once this configuration is done. We can move on to run our python script. + +************************************************* +Procedure to automatically deploy the helm chart +************************************************* + +Steps: + +1. After activating your virtual environment, run the ``main.py``. + +.. code-block:: console + + python main.python + +2. Enter the location of the chart you want to deploy. + +For example, lets deploy proxchart + +.. code-block:: console + + Enter the location of helm chart: ../charts/proxchart + +3. The last command will execute the entire ``main.py`` and will return all the required information about the chart. + +******* +Output +******* + +.. code-block:: console + + Status of helm charts + + NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION + proxchart default 1 2021-09-27 13:22:21.864816 +0530 IST deployed proxchart-0.1.0 1.0 + ---------------------------------------------------------------------------------------------------- + + POD DETAILS + + ┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳ + ┃ POD NAME ┃ NAMESPACE ┃ HOST-IP ┃ PHASE ┃ POD-IP ┃ POD-IPs ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ prox-6db7c6dc9b-l42v4 │ default │ 192.168.49.2 │ Running │ 172.17.0.4 │ [{'ip': '172.17.0.4'}] │ + └───────────────────────┴───────────┴──────────────┴─────────┴────────────┴────────────────────────┴ + ┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ POD NAME ┃ INTERFACE IPs ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ prox-6db7c6dc9b-l42v4 │ 127.0.0.1/8, 172.17.0.4/16, 127.0.0.1, 172.17.0.4, 172.17.255.255 │ + └───────────────────────┴───────────────────────────────────────────────────────────────────┘ + + DEPLOYMENT DETAILS + + ┏━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓ + ┃ NAME ┃ Type ┃ CLUSTER-IP ┃ EXTERNAL-IP ┃ PORT(S) ┃ + ┡━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩ + │ proxchart │ NodePort │ 10.103.156.83 │ {} │ 8081:31036/TCP │ + └───────────┴──────────┴───────────────┴─────────────┴────────────────┘ + +******************** +Future Enhancements +******************** + +In future, more information can be extracted by adding new functions to the file. The process of getting interfaces using regex can be made more proficient.
\ No newline at end of file diff --git a/tools/k8s/app-deployment/helm/charts/mynet.yaml b/tools/k8s/app-deployment/helm/charts/mynet.yaml new file mode 100644 index 00000000..ca3af9a7 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/mynet.yaml @@ -0,0 +1,20 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: mynet +spec: + config: '{ + "cniVersion":"0.3.0" + "name":"mynet" + "plugins":[ + { + "bridge":"kube-bridge", + "ipam":{ + "subnet":10.244.0.0/24", + "type":"host-local" + }, + "isDefaultGateway":true, + "type":"bridge" + } + ] + }' diff --git a/tools/k8s/app-deployment/helm/charts/pod.yaml b/tools/k8s/app-deployment/helm/charts/pod.yaml new file mode 100644 index 00000000..942c8f97 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/pod.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx + annotations: + k8s.v1.cni.cncf.io/networks: mynet + +spec: + containers: + - name: prox + image: opnfv/rapid:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + - name: nginx + image: nginx:alpine + ports: + - containerPort: 80 diff --git a/tools/k8s/app-deployment/helm/charts/proxchart/Chart.yaml b/tools/k8s/app-deployment/helm/charts/proxchart/Chart.yaml new file mode 100644 index 00000000..6acc06f4 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/proxchart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: proxchart +version: 0.1.0 +appVersion: "1.0" +description: To deploy prox diff --git a/tools/k8s/app-deployment/helm/charts/proxchart/templates/daemonset.yaml b/tools/k8s/app-deployment/helm/charts/proxchart/templates/daemonset.yaml new file mode 100644 index 00000000..e1d3a563 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/proxchart/templates/daemonset.yaml @@ -0,0 +1,25 @@ +{{- if eq .Values.kind "DaemonSet"}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: prox + annotations: + k8s.v1.cni.cncf.io/networks: "{{- join "\",\"" .Values.extraInterfaces }}" +spec: + selector: + matchLabels: + app: prox + template: + metadata: + labels: + app: prox + spec: + containers: + - name: prox + image: opnfv/rapid:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + nodeSelector: + {{- .Values.nodeSelector.matchLables | toYaml | nindent 12 }} +{{- end }} diff --git a/tools/k8s/app-deployment/helm/charts/proxchart/templates/deployment.yaml b/tools/k8s/app-deployment/helm/charts/proxchart/templates/deployment.yaml new file mode 100644 index 00000000..235b02cd --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/proxchart/templates/deployment.yaml @@ -0,0 +1,30 @@ +{{- if eq .Values.kind "Deployment"}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prox + annotations: + k8s.v1.cni.cncf.io/networks: "{{- join "\",\"" .Values.extraInterfaces | toYaml | nindent 8}}" +spec: + selector: + matchLabels: + app: prox + replicas: {{ .Values.replicas }} + template: + metadata: + labels: + app: prox + spec: + containers: + - name: prox + image: opnfv/rapid:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + - name: nginx + image: nginx:alpine + ports: + - containerPort: 80 + nodeSelector: + {{- .Values.nodeSelector.matchLables | toYaml | nindent 12 }} +{{- end }} diff --git a/tools/k8s/app-deployment/helm/charts/proxchart/templates/service.yaml b/tools/k8s/app-deployment/helm/charts/proxchart/templates/service.yaml new file mode 100644 index 00000000..15215f35 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/proxchart/templates/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: proxchart +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http diff --git a/tools/k8s/app-deployment/helm/charts/proxchart/values.yaml b/tools/k8s/app-deployment/helm/charts/proxchart/values.yaml new file mode 100644 index 00000000..405e4c25 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/proxchart/values.yaml @@ -0,0 +1,20 @@ +# daemonset, deployment +kind: Deployment + +# Nodes to deploy on +nodeSelector: {} + # matchLables: + # key1: value1 + # key2: value2 + +# multus +metacniType: multus +extraInterfaces: + net-attach-def1 + net-attach-def2 + +replicas: 1 + +service: + type: NodePort + port: 8081 diff --git a/tools/k8s/app-deployment/helm/charts/testpmdchart/Chart.yaml b/tools/k8s/app-deployment/helm/charts/testpmdchart/Chart.yaml new file mode 100644 index 00000000..85e8f7d0 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/testpmdchart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: testpmdchart +version: 0.1.0 +appVersion: "1.0" +description: To deploy test pmd diff --git a/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/daemonset.yaml b/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/daemonset.yaml new file mode 100644 index 00000000..10a812f6 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/daemonset.yaml @@ -0,0 +1,25 @@ +{{- if eq .Values.kind "DaemonSet"}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: testpmd + annotations: + k8s.v1.cni.cncf.io/networks: "{{- join "\",\"" .Values.extraInterfaces }}" +spec: + selector: + matchLabels: + app: testpmd + template: + metadata: + labels: + app: testpmd + spec: + containers: + - name: testpmd + image: ovsperf/dpdkfwd:lakelse + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + nodeSelector: + {{- .Values.nodeSelector.matchLables | toYaml | nindent 12 }} +{{- end }} diff --git a/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/deployment.yaml b/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/deployment.yaml new file mode 100644 index 00000000..6df47ff1 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/deployment.yaml @@ -0,0 +1,26 @@ +{{- if eq .Values.kind "Deployment"}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: testpmd + annotations: + k8s.v1.cni.cncf.io/networks: "{{- join "\",\"" .Values.extraInterfaces | toYaml | nindent 8}}" +spec: + selector: + matchLabels: + app: testpmd + replicas: {{ .Values.replicas }} + template: + metadata: + labels: + app: testpmd + spec: + containers: + - name: testpmd + image: vsperf/dpdkfwd:lakelse + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + nodeSelector: + {{- .Values.nodeSelector.matchLables | toYaml | nindent 12 }} +{{- end }} diff --git a/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/service.yaml b/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/service.yaml new file mode 100644 index 00000000..50f75629 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/testpmdchart/templates/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: testpmdchart +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http diff --git a/tools/k8s/app-deployment/helm/charts/testpmdchart/values.yaml b/tools/k8s/app-deployment/helm/charts/testpmdchart/values.yaml new file mode 100644 index 00000000..af6be613 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/testpmdchart/values.yaml @@ -0,0 +1,20 @@ +# daemonset, deployment +kind: Deployment + +# Nodes to deploy on +nodeSelector: + # matchLables: + # key1: value1 + # key2: value2 + +# multus +metacniType: multus +extraInterfaces: + net-attach-def1 + net-attach-def2 + +replicas: 1 + +service: + type: NodePort + port: 8081 diff --git a/tools/k8s/app-deployment/helm/charts/trexchart/Chart.yaml b/tools/k8s/app-deployment/helm/charts/trexchart/Chart.yaml new file mode 100644 index 00000000..1dfb7b0b --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/trexchart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: trexchart +description: A Helm chart for trex Deployment +version: 0.1.0 +appVersion: "1.0" diff --git a/tools/k8s/app-deployment/helm/charts/trexchart/templates/daemonset.yaml b/tools/k8s/app-deployment/helm/charts/trexchart/templates/daemonset.yaml new file mode 100644 index 00000000..39d4662f --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/trexchart/templates/daemonset.yaml @@ -0,0 +1,25 @@ +{{- if eq .Values.kind "DaemonSet"}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: trex + annotations: + k8s.v1.cni.cncf.io/networks: "{{- join "\",\"" .Values.extraInterfaces }}" +spec: + selector: + matchLabels: + app: trex + template: + metadata: + labels: + app: trex + spec: + containers: + - name: trex + image: vsperf/trex:lakelse + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + nodeSelector: + {{- .Values.nodeSelector.matchLables | toYaml | nindent 12 }} +{{- end }} diff --git a/tools/k8s/app-deployment/helm/charts/trexchart/templates/deployment.yaml b/tools/k8s/app-deployment/helm/charts/trexchart/templates/deployment.yaml new file mode 100644 index 00000000..8b0f447e --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/trexchart/templates/deployment.yaml @@ -0,0 +1,26 @@ +{{- if eq .Values.kind "Deployment"}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: trex + annotations: + k8s.v1.cni.cncf.io/networks: "{{- join "\",\"" .Values.extraInterfaces | toYaml | nindent 8}}" +spec: + selector: + matchLabels: + app: trex + replicas: {{ .Values.replicas }} + template: + metadata: + labels: + app: trex + spec: + containers: + - name: trex + image: vsperf/trex:lakelse + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + nodeSelector: + {{- .Values.nodeSelector.matchLables | toYaml | nindent 12 }} +{{- end }} diff --git a/tools/k8s/app-deployment/helm/charts/trexchart/templates/service.yaml b/tools/k8s/app-deployment/helm/charts/trexchart/templates/service.yaml new file mode 100644 index 00000000..8965194d --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/trexchart/templates/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: trexchart +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http diff --git a/tools/k8s/app-deployment/helm/charts/trexchart/values.yaml b/tools/k8s/app-deployment/helm/charts/trexchart/values.yaml new file mode 100644 index 00000000..ed289cf9 --- /dev/null +++ b/tools/k8s/app-deployment/helm/charts/trexchart/values.yaml @@ -0,0 +1,20 @@ +# daemonset, deployment +kind: DaemonSet + +# Nodes to deploy on +nodeSelector: + # matchLables: + # key1: value1 + # key2: value2 + +# multus +metacniType: multus +extraInterfaces: + net-attach-def1 + net-attach-def2 + +replicas: 1 + +service: + type: NodePort + port: 8081 diff --git a/tools/k8s/app-deployment/helm/pyscript/Pipfile b/tools/k8s/app-deployment/helm/pyscript/Pipfile new file mode 100644 index 00000000..dd60181c --- /dev/null +++ b/tools/k8s/app-deployment/helm/pyscript/Pipfile @@ -0,0 +1,18 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +kubernetes = "*" +pyyaml = "*" +pick = "*" +simplejson = "*" +nested-lookup = "*" +rich = "*" +pylint = "*" + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/tools/k8s/app-deployment/helm/pyscript/main.py b/tools/k8s/app-deployment/helm/pyscript/main.py new file mode 100644 index 00000000..1419f345 --- /dev/null +++ b/tools/k8s/app-deployment/helm/pyscript/main.py @@ -0,0 +1,172 @@ +# Copyright 2015-2017 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +''' +This script automatically develop helm charts and returns +useful informations +''' + +import os +import subprocess +from subprocess import PIPE, STDOUT +import time +import re +from ast import literal_eval +import yaml +from rich.console import Console +from rich.table import Table + +console = Console() + +def check_system_installations(): + ''' Check system installations necessary for successful run of this script ''' + try: + subprocess.run("helm version",shell=True,check=True) + subprocess.run("kubectl version --client",shell=True, + check= True) + subprocess.run("minikube version",shell=True,check=True) + + except subprocess.CalledProcessError as error: + print (error.output) + + +def parse_helm_chart(helm_path): + ''' Extract name of the chart ''' + values_file = os.path.join(helm_path,"Chart.yaml") + with open(values_file, 'r', encoding='utf-8') as file: + doc = yaml.load(file) + name_of_chart = doc['name'] + + values_file = os.path.join(helm_path,"values.yaml") + with open(values_file, 'r',encoding='utf-8') as file: + doc = yaml.load(file) + n_pods = doc['replicas'] + return (name_of_chart, n_pods) + + +def service_details(name): + ''' Extract Service Details''' + print("\nDEPLOYEMENT DETAILS\n") + + with subprocess.Popen(f"kubectl get service -o json {name}",shell=True, + stdin=PIPE, stdout=PIPE, stderr=STDOUT) as process: + output = process.stdout.read() + string = output.decode().replace("'", '"') + _j = literal_eval(string) + + table = Table(show_header=True) + + table.add_column("NAME") + table.add_column("Type") + table.add_column("CLUSTER-IP") + table.add_column("EXTERNAL-IP") + table.add_column("PORT(S)") + + col=f'{_j["spec"]["ports"][0]["port"]}:{_j["spec"]["ports"][0]["nodePort"]}/{_j["spec"]["ports"][0]["protocol"]}' + + table.add_row( + f'{_j["metadata"]["name"]}', + f'{_j["spec"]["type"]}', + f'{_j["spec"]["clusterIP"]}', + f'{_j["status"]["loadBalancer"]}', + col, + ) + console.print(table) + + +def pod_details(replicas): + ''' Extract Pod Details''' + print("\nPOD DETAILS\n") + with subprocess.Popen("kubectl get pods -o json", shell=True, + stdin=PIPE, stdout=PIPE, stderr=STDOUT) as process: + output = process.stdout.read() + pod_string = output.decode().replace("'", '"') + # true = True + # null = None + # false = False + pod_json = literal_eval(pod_string) + + table = Table(show_header=True) + + table.add_column("POD NAME") + table.add_column("NAMESPACE") + table.add_column("HOST-IP") + table.add_column("PHASE") + table.add_column("POD-IP") + table.add_column("POD-IPs") + podname = pod_json["items"][0]["metadata"]["name"] + + for i in range(replicas): + table.add_row( + f'{pod_json["items"][i]["metadata"]["name"]}', + f'{pod_json["items"][i]["metadata"]["namespace"]}', + f'{pod_json["items"][i]["status"]["hostIP"]}', + f'{pod_json["items"][i]["status"]["phase"]}', + f'{pod_json["items"][i]["status"]["podIP"]}', + f'{pod_json["items"][i]["status"]["podIPs"]}', + ) + console.print(table) + + ip_interface(podname) + + +def ip_interface(podname): + ''' Extract Network IP''' + with subprocess.Popen(f"kubectl exec -i {podname} -c nginx -- ip -o a", shell=True, + stdin=PIPE, stdout=PIPE, stderr=STDOUT) as process: + output = process.stdout.read() + _string = output.decode().replace("'", '"') + ipregex = r"((?:[0-9]{1,3}[.]){3}[0-9]{1,3}/[0-9]{1,2})" + ipregex2 = r"((?:[0-9]{1,3}[.]){3}[0-9]{1,3})" + list1=re.findall(ipregex, _string) + list2=re.findall(ipregex2 ,_string) + ip_list = list1 + list2 + ip_string = ', '.join(ip_list) + + table = Table(show_header=True) + + table.add_column("POD NAME") + table.add_column("INTERFACE IPs") + + table.add_row( + podname, + ip_string, + ) + console.print(table) + + +def main(): + ''' Main Function ''' + check_system_installations() + + helm_location = input("Enter the location of helm chart: ") + name,replicas = parse_helm_chart(helm_location) + + subprocess.run(f"helm install {name} {helm_location}",shell=True, + check = True).stdout.read() + time.sleep(10) + + # status of helm charts + print("\nStatus of helm charts\n") + subprocess.Popen("helm list", shell =True,stdout=subprocess.PIPE,).communicate() + print("--" * 50) + + #pod details + pod_details(replicas) + + #deployment details + service_details(name) + +if __name__ == "__main__": + main() |