This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Getting Started

Get started with Kuesta.

    This tutorial shows you how to

    1. Create a local Kubernetes cluster.
    2. Install Kuesta.
    3. Run device emulator.
    4. Create new service and configure the device emulators by GitOps.

    Prerequisites

    1. Install golang (v1.20.4+) to use controller-tools.
    2. Install docker (v20.10.24+) to use cluster in kind.
    3. Install kind (v0.12.0+) to set up local Kubernetes cluster.
    4. Install kubectl (v1.25.4+) to run commands against Kubernetes clusters.
    5. Install cue (v0.4.x) to run Kuesta installation script.
    6. Install gnmic (v0.26.0+) to perform gNMI request to kuesta server.

    Create a local Kubernetes cluster

    Create a local Kubernetes cluster with kind.

    kind create cluster
    

    You can check that the cluster was successfully created by running:

    kubectl cluster-info
    

    Then you will see an output similar to the following that confirms your local cluster is running:

    Kubernetes control plane is running at https://127.0.0.1:52096
    CoreDNS is running at https://127.0.0.1:52096/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
    

    Set up GitHub source repositories for testing

    To configure network devices using Kuesta’s GitOps, you need to set up 2 GitHub repositories. One is for the configuration source repository, and the other is for the update history of network devices’ actual configuration status. You can easily set up these repositories using Kuesta Example repository as a template. Repository must consist of lower case alphanumeric characters, ‘-’ or ‘.’, and must start and end with an alphanumeric character.

    Run the following 2 times to create the repository pair:

    1. Click Use this template button of Kuesta Example repository.

    2. Select a name for your new repository and click Create repository from template. You can choose either public or private whichever you prefer.

    Install Kuesta and sample resources

    To install Kuesta and sample resources like device drivers and emulators, just run cue script:

    git clone https://github.com/nttcom/kuesta.git
    cd kuesta/tool/install
    cue install
    

    Provide required parameters according to the instructions: The GitHub repository for config GitHub repository for status should specify each of the two Github repositories for verification that you have created.

    GitHub repository for config: https://github.com/<your_org>/<your_config_repo>
    GitHub repository for status: https://github.com/<your_org>/<your_status_repo>
    GitHub username: <your_username>
    GitHub private access token: <your_private_access_token>
    Are these git repositories private? (yes|no): yes
    Do you need sample driver and emulator for trial?: (yes|no) yes
    
    ---
    GitHub Config Repository: https://github.com/<your_org>/<your_config_repo>
    GitHub Status Repository: https://github.com/<your_org>/<your_status_repo>
    GitHub Username: <your_username>
    GitHub Access Token: ***
    Use Private Repo: true
    
    Image Registry: ghcr.io/nttcom/kuesta
    Version: latest
    Deploy sample driver and emulator: true
    ---
    
    Continue? (yes|no) yes
    

    For Kuesta to perform git-push and create pull-request, you need to prepare a GitHub personal access token (PAT) and provide it in the installation(classic is recommended). Since PAT provided here is stored only in Secret resources on your local Kubernetes cluster, you can remove them safely and completely by cleaning up your local cluster.

    After running an installation script, you can see what is installed by running kubectl commands:

    1: Custom Resource Definitions(CRDs):

    kubectl get customresourcedefinitions.apiextensions.k8s.io
    
    NAME                                        CREATED AT
    buckets.source.toolkit.fluxcd.io            2023-01-10T06:31:20Z
    certificaterequests.cert-manager.io         2023-01-10T06:31:19Z
    certificates.cert-manager.io                2023-01-10T06:31:19Z
    challenges.acme.cert-manager.io             2023-01-10T06:31:19Z
    clusterissuers.cert-manager.io              2023-01-10T06:31:19Z
    devicerollouts.kuesta.hrk091.dev            2023-01-10T06:43:30Z
    gitrepositories.source.toolkit.fluxcd.io    2023-01-10T06:31:20Z
    helmcharts.source.toolkit.fluxcd.io         2023-01-10T06:31:20Z
    helmrepositories.source.toolkit.fluxcd.io   2023-01-10T06:31:20Z
    issuers.cert-manager.io                     2023-01-10T06:31:19Z
    ocdemoes.kuesta.hrk091.dev                  2023-01-10T06:43:36Z
    ocirepositories.source.toolkit.fluxcd.io    2023-01-10T06:31:20Z
    orders.acme.cert-manager.io                 2023-01-10T06:31:19Z
    

    Some CRDs belonging to kuesta.hrk091.dev, source.toolkit.fluxcd.io, cert-manager.io are installed.

    2: Pods:

    kubectl get pods -A | grep -v 'kube-system'
    
    NAMESPACE                NAME                                                  READY   STATUS    RESTARTS   AGE
    cert-manager             cert-manager-7475574-t7qlm                            1/1     Running   0          12m
    cert-manager             cert-manager-cainjector-d5dc6cd7f-wbh6r               1/1     Running   0          12m
    cert-manager             cert-manager-webhook-6868bd8b7-kc85s                  1/1     Running   0          12m
    device-operator-system   device-operator-controller-manager-5d8648469b-57x4l   2/2     Running   0          11s
    flux-system              source-controller-7ff779586b-wsgf7                    1/1     Running   0          12m
    kuesta-getting-started   gnmi-fake-oc01-79d4d679b4-tvm78                       1/1     Running   0          10s
    kuesta-getting-started   gnmi-fake-oc02-69d9cc664d-56bsz                       1/1     Running   0          10s
    kuesta-getting-started   subscriber-oc01                                       1/1     Running   0          9s
    kuesta-getting-started   subscriber-oc02                                       1/1     Running   0          9s
    kuesta-system            kuesta-aggregator-7586999c47-jj7m4                    1/1     Running   0          27s
    kuesta-system            kuesta-server-85fc4f8646-kq72c                        1/1     Running   0          27s
    local-path-storage       local-path-provisioner-9cd9bd544-lk9w9                1/1     Running   0          16m
    provisioner-system       provisioner-controller-manager-7675d487c8-w6f7x       1/2     Running   0          17s
    

    When all pods show Running status, you are ready to continue.

    Kuesta core services are deployed in the following namespaces:

    • kuesta-system
      • Kuesta Server which provides core features and serves API.
    • provisioner-system
      • Kubernetes custom operator used for GitOps and multi-device transaction management.
    • flux-system
      • Flux source-controller and Custom Resources to integrate with manifest sources.
    • cert-manager
      • Act as a private CA and issue certificates for mTLS.

    The resources for this getting-started are deployed as well in the following namespaces:

    • device-operator-system
      • Kubernetes custom operator as a device driver to configure device emulator.
    • kuesta-getting-started
      • Device emulators for this getting started, and Kubernetes custom resources to trigger device configuration.

    Following 2 pods are device emulators which can be configured by OpenConfig over gNMI, named oc01 and oc02.

    kuesta-getting-started   gnmi-fake-oc01-79d4d679b4-tvm78                       1/1     Running   0          10s
    kuesta-getting-started   gnmi-fake-oc02-69d9cc664d-56bsz                       1/1     Running   0          10s
    

    If you want to check what configurations are stored in these emulators, you can easily do it by port-forwarding and requesting to these emulator pods as follows:

    # Get oc01 emulator's configuration
    kubectl -n kuesta-getting-started port-forward svc/gnmi-fake-oc01 9339:9339
    gnmic -a :9339 -u admin -p admin get --path "" --encoding JSON --skip-verify
    

    Request Service configuration and run GitOps

    A Service is a abstracted high-level model of the network configuration, and you can configure multiple devices by creating/updating Services.

    In the sample repository there is a sample Service named oc-circuit, you can try it as a demonstration. Service is written by CUE. For example, oc-circuit service is as follows:

    package oc_circuit
    
    import (
    	ocdemo "github.com/hrk091/kuesta-testdata/pkg/ocdemo"
    )
    
    #Input: {
    	// kuesta:"key=1"
    	vlanID: uint32
    	endpoints: [...{
    		device: string
    		port:   uint32
    	}]
    }
    
    #Template: {
    	input: #Input
    
    	output: devices: {
    		for _, ep in input.endpoints {
    			"\(ep.device)": config: {
    				ocdemo.#Device
    
    				let _portName = "Ethernet\(ep.port)"
    				Interface: "\(_portName)": SubInterface: "\(input.vlanID)": {
    					Ifindex:     ep.port
    					Index:       input.vlanID
    					Name:        "\(_portName).\(input.vlanID)"
    					AdminStatus: 1
    					OperStatus:  1
    				}
    
    				Vlan: "\(input.vlanID)": {
    					VlanId: input.vlanID
    					Name:   "VLAN\(input.vlanID)"
    					Status: ocdemo.#Vlan_Status_ACTIVE
    					Tpid:   ocdemo.#OpenconfigVlanTypes_TPID_TYPES_UNSET
    				}
    			}
    		}
    	}
    }
    

    The oc-circuit Service will conduct mapping from #Template.input (Service config) to the #Template.output (multiple network device configs) as defined in #Template, then it will generate simple vlan and vlan sub-interface configurations. To use oc-circuit Service, you need to provide vlanID and multiple device ports according to the #Input type definition.

    You can create the oc-circuit Service by the following steps:

    1: Create a request json file named oc-circuit-vlan100.json with the following content:

    {
        "vlanID": 100,
        "endpoints": [
            {
                "device": "oc01",
                "port": 1
            },
            {
                "device": "oc02",
                "port": 1
            }
        ]
    }
    

    When using the http interface, please create the following JSON instead of the above JSON. (Specify the path of the service in “path” and the value of the service to be submitted in “value.)

    {
        "path": "/services/oc_circuit/100",
        "value": {
            "vlanID": 100,
            "endpoints": [
                {
                    "device": "oc01",
                    "port": 1
                },
                {
                    "device": "oc02",
                    "port": 1
                }
            ]
        }
    }
    

    This sample Service requests both oc01 and oc02 device emulators to create a VLAN sub-interface with vlanID=100 on port 1.

    2: Port-forward to the kuesta-server Pod running on your local Kubernetes.

    kubectl -n kuesta-system port-forward svc/kuesta-server 9339:9339
    

    If the http interface is used, the following is the case.

    kubectl -n kuesta-system port-forward svc/kuesta-server 8080:8080
    

    3: Send gNMI set request to kuesta-server using gnmic.

    gnmic -a :9339 -u dummy -p dummy set --replace-path "/services/service[kind=oc_circuit][vlanID=100]" --encoding JSON --insecure --replace-file oc-circuit-vlan100.json
    

    If you use the http interface, send the following POST Request to the Questa server using curl.

    curl -X POST -H "Content-Type: application/json" -d @oc-circuit-vlan100.json http://localhost:8080/set
    

    4: Check the PullRequest(PR) in your configuration repository on GitHub web console. Access PR list then you will find the PR which titles as [kuesta] Automated PR. You can see what services and devices are configured by this PR on the PR comment, and their details from the Files changed tab.

    5: Before merging PR branch, it is better to monitor DeviceRollout resource, which conducts a device configuration rollout. Run monitor with kubectl:

    kubectl -n kuesta-getting-started get devicerollouts.kuesta.hrk091.dev -w
    

    If you see there is one DeviceRollout resource and its STATUS is Completed, you are ready to run GitOps delivery procedure.

    NAME              PHASE     STATUS
    kuesta-testdata   Healthy   Completed
    

    6: Merge the PR on GitHub web console, and go to the terminal where DeviceRollout monitor running. In one minute, you will see DeviceRollout status changes to Running, then changes back to Completed again.

    NAME              PHASE     STATUS
    kuesta-testdata   Healthy   Completed
    kuesta-testdata   Healthy   Completed
    kuesta-testdata   Healthy   Running
    kuesta-testdata   Healthy   Running
    kuesta-testdata   Healthy   Running
    kuesta-testdata   Healthy   Completed
    

    7: Check the updated device configuration. Get entire device config by sending gNMI get request to kuesta-server with gnmic:

    gnmic -a :9339 -u admin -p admin get --path "/devices/device[name=oc01]" --path "/devices/device[name=oc02]" --encoding JSON --insecure
    

    If you use the http interface, send the following POST Request to the Questa server using curl.

    curl -X POST -H "Content-Type: application/json" -d '{"paths": ["/devices/oc01", "/devices/oc02"]}'  http://localhost:8080/get
    

    The output displays the gNMI response payload like:

    [
      {
        "source": ":9339",
        "timestamp": 1673360105000000000,
        "time": "2023-01-10T23:15:05+09:00",
        "updates": [
          {
            "Path": "devices/device[name=oc01]",
            "values": {
              "devices/device": {
                "Interface": {
                  "Ethernet1": {
                    "AdminStatus": 1,
                    "Description": "awesome interface!!",
                    "Enabled": false,
                    "Mtu": 9000,
                    "Name": "Ethernet1",
                    "OperStatus": 1,
                    "Subinterface": {
                      "100": {
                        "AdminStatus": 1,
                        "Ifindex": 1,
                        "Index": 100,
                        "Name": "Ethernet1.100",
                        "OperStatus": 1
                      },
                      ...
                    },
                    ...
                  },
                  ...
                },
                "Vlan": {
                  "100": {
                    "Member": [],
                    "Name": "VLAN100",
                    "Status": 1,
                    "Tpid": 0,
                    "VlanId": 100
                  },
                  ...
    

    The response when using the http interface is as follows.

    [
        {
            "Interface": {
                "Ethernet1": {
                    "AdminStatus": 1,
                    "Description": "",
                    "Enabled": false,
                    "Mtu": 9000,
                    "Name": "Ethernet1",
                    "OperStatus": 1,
                    "SubInterface": {
                        "100": {
                            "AdminStatus": 1,
                            "Ifindex": 1,
                            "Index": 100,
                            "Name": "Ethernet1.100",
                            "OperStatus": 1
                        }
                    },
                    "Subinterface": {},
                    "Type": 80
                },
                "Ethernet2": {
                    "AdminStatus": 1,
                    "Description": "",
                    "Enabled": false,
                    "Mtu": 9000,
                    "Name": "Ethernet2",
                    "OperStatus": 1,
                    "Subinterface": {},
                    "Type": 80
                },
                "Ethernet3": {
                    "AdminStatus": 1,
                    "Description": "",
                    "Enabled": false,
                    "Mtu": 9000,
                    "Name": "Ethernet3",
                    "OperStatus": 1,
                    "Subinterface": {},
                    "Type": 80
                }
            },
            "Vlan": {
                "100": {
                    "Member": [],
                    "Name": "VLAN100",
                    "Status": 1,
                    "Tpid": 0,
                    "VlanId": 100
                }
            }
        },
        {
            "Interface": {
                "Ethernet1": {
                    "AdminStatus": 1,
                    "Description": "",
                    "Enabled": false,
                    "Mtu": 9000,
                    "Name": "Ethernet1",
                    "OperStatus": 1,
                    "SubInterface": {
                        "100": {
                            "AdminStatus": 1,
                            "Ifindex": 1,
                            "Index": 100,
                            "Name": "Ethernet1.100",
                            "OperStatus": 1
                        }
                    },
                    "Subinterface": {},
                    "Type": 80
                },
                "Ethernet2": {
                    "AdminStatus": 1,
                    "Description": "",
                    "Enabled": false,
                    "Mtu": 9000,
                    "Name": "Ethernet2",
                    "OperStatus": 1,
                    "Subinterface": {},
                    "Type": 80
                },
                "Ethernet3": {
                    "AdminStatus": 1,
                    "Description": "",
                    "Enabled": false,
                    "Mtu": 9000,
                    "Name": "Ethernet3",
                    "OperStatus": 1,
                    "Subinterface": {},
                    "Type": 80
                }
            },
            "Vlan": {
                "100": {
                    "Member": [],
                    "Name": "VLAN100",
                    "Status": 1,
                    "Tpid": 0,
                    "VlanId": 100
                }
            }
        }
    ]
    

    You will find that vlan and vlan sub-interfaces of both devices oc01 and oc02 are configured correctly as implemented in the oc-circuit service model. In addition, these device config changes are committed and pushed to your status repository, you can find these device config updates from the main branch head.

    Clean up

    To delete the local cluster you created for this getting-started, just run:

    kind delete cluster
    

    Do not forget to remove your PAT to access your private repository if no longer needed.