Skip to content

Commit

Permalink
feat: PowerCLI example function to enforce VDS pg configs
Browse files Browse the repository at this point in the history
Closes #771
Signed-off-by: Patrick Kremer <pkremer@vmware.com>
  • Loading branch information
Patrick Kremer committed Feb 7, 2022
1 parent 02101d0 commit fdf5c64
Show file tree
Hide file tree
Showing 9 changed files with 508 additions and 0 deletions.
5 changes: 5 additions & 0 deletions examples/knative/powercli/kn-pcli-vds-pg-config/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM us.gcr.io/daisy-284300/veba/ce-pcli-base:1.4

COPY handler.ps1 handler.ps1

CMD ["pwsh","./server.ps1"]
187 changes: 187 additions & 0 deletions examples/knative/powercli/kn-pcli-vds-pg-config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# kn-pcli-vds-pg-config
Example Knative PowerCLI function for enforcing specific VDS portgroup configuration - automatically reset portgroup `Notify Switches` value to desired state

# Step 1 - Build

> **Note:** This step is only required if you made code changes to `handler.ps1`
> or `Dockerfile`.
Create the container image locally to test your function logic.

Mac/Linux
```
# change the IMAGE name accordingly, example below for Docker
export IMAGE=<docker-username>/kn-pcli-vds-pg-config:1.0
docker build -t ${IMAGE} .
```

Windows
```
# change the IMAGE name accordingly, example below for Docker
$IMAGE="<docker-username>/kn-pcli-vds-pg-config:1.0"
docker build -t ${IMAGE} .
```
# Step 2 - Test

Verify the container image works by executing it locally.

Change into the `test` directory
```console
cd test
```

Update the following variable names within the `docker-test-env-variable` file

* `VCENTER_SERVER` - IP Address or FQDN of the vCenter Server to connect to
* `VCENTER_USERNAME` - vCenter account with permission to reconfigure distributed virtual switches
* `VCENTER_PASSWORD` - vCenter password associated with the username
* `VCENTER_CERTIFCATE_ACTION` - Set-PowerCLIConfiguration Action to configure when connection fails due to certificate error, default is Fail. (Possible values: Fail, Ignore or Warn)
* `NOTIFY_SWITCHES` - Enforce this teaming policy configuration (Possible values: True, False))

If you built a custom image in Step 1, comment out the default `IMAGE` command below - the `docker run` command will then use use the value previously stored in the `IMAGE` variable. Otherwise, use the default image as shown below. Start the container image by running the following commands:

Mac/Linux
```console
export IMAGE=us.gcr.io/daisy-284300/veba/kn-pcli-vds-pg-config:1.0
docker run -e FUNCTION_DEBUG=true -e PORT=8080 --env-file docker-test-env-variable -it --rm -p 8080:8080 ${IMAGE}
```
Windows
```console
IMAGE="us.gcr.io/daisy-284300/veba/kn-pcli-vds-pg-config:1.0"
docker run -e FUNCTION_DEBUG=true -e PORT=8080 --env-file docker-test-env-variable -it --rm -p 8080:8080 ${IMAGE}
```

In the `test` directory, edit `test-payload.json`. Locate the `Dvs` section of the JSON file. Change the `Name:` property from `REPLACE-ME` to the name of a distributed virtual switch currently in your vCenter inventory. Locate the `Net` section of the JSON file. Change the `Name:` property to the name of a distributed portgroup on the distributed virtual switch from the previous step. If you do not make these changes, the function will still be invoked, but the configuration operation will fail because the objects will not be found.

```json
"Net": {
"Name": "REPLACE-ME",
"Network": {
"Type": "DistributedVirtualPortgroup",
"Value": "dvportgroup-7007"
}
},
"Dvs": {
"Name": "REPLACE-ME",
"Dvs": {
"Type": "DistributedVirtualSwitch",
"Value": "dvs-7005"
}
}
```
**WARNING** - This function will reconfigure your distributed virtual switch - it will set the `Notify Switches` property of the portgroup to the value specified in `vds_pg_secret.json`

In a separate terminal, run either `send-cloudevent-test.ps1` (PowerShell Script) or `send-cloudevent-test.sh` (Bash Script) to simulate a CloudEvent payload being sent to the local container image

```console
Testing Function ...
See docker container console for output

# Output from docker container console
05/26/2021 13:46:44 - PowerShell HTTP server start listening on 'http://*:8080/'
05/26/2021 13:46:44 - Processing Init

05/26/2021 13:46:44 - Configuring PowerCLI Configuration Settings

05/26/2021 13:46:44 - Connecting to vCenter Server vcsa.primp-industries.local

05/26/2021 13:47:19 - Successfully connected to vcsa.primp-industries.local

05/26/2021 13:47:19 - Init Processing Completed

05/26/2021 13:47:32 - Processing Handler

05/26/2021 13:47:32 - Start CloudEvent Decode

05/26/2021 13:47:32 - CloudEvent Decode Complete

DEBUG: K8s Secrets:
{"VCENTER_SERVER":"vcsa.primp-industries.local","VCENTER_USERNAME":"administrator@vsphere.local","VCENTER_PASSWORD":"****","VCENTER_TAG_NAME":"Demo"}

DEBUG: CloudEventData

Name Value
---- -----
Dvs {Dvs, Name}
UserName VSPHERE.LOCAL\Administrator
Host
ConfigSpec {MaxPorts, ExtensionKey, Host, Name…}
Key 567176
ComputeResource
Ds
Vm
ConfigChanges {Modified, Added, Deleted}
ChangeTag
Net
CreatedTime 12/19/2021 21:01:35
FullFormattedMessage The vSphere Distributed Switch dvsTest in HomeLab was reconfigured. …
Datacenter {Datacenter, Name}
ChainId 567175

Found VDS name dvsTest
Setting Notify Switches to False

12/19/2021 21:01:39 - VDS portgroup reconfig operation complete ...

12/19/2021 21:01:39 - Handler Processing Completed ...
```

> Pro Tip - If you are rapidly iterating on the code and want to easily rebuild and launch the container,
> you can chain all of the commands together with ampersands. This will allow you to re-run
> the commands by simply pressing the `up` arrow and `Enter`.
```console
cd .. && docker build -t ${IMAGE} . && cd test && docker run -e FUNCTION_DEBUG=true -e PORT=8080 --env-file docker-test-env-variable -it --rm -p 8080:8080 ${IMAGE}
```
# Step 3 - Deploy

> **Note:** The following steps assume a working Knative environment using the
`default` Rabbit `broker`. The Knative `service` and `trigger` will be installed in the
`vmware-functions` Kubernetes namespace, assuming that the `broker` is also available there.

If you built a custom image, push it to an accessible registry such as Docker once you're done developing and testing your function logic.

```console
docker push ${IMAGE}
```

Update the `vds_pg_secret.json` file with your vCenter Server credentials and configurations and then create the kubernetes secret which can then be accessed from within the function by using the environment variable named called `VDS_SECRET`.

```console
# create secret

kubectl -n vmware-functions create secret generic vds-pg-secret --from-file=VDS_SECRET=vds_pg_secret.json

# update label for secret to show up in VEBA UI
kubectl -n vmware-functions label secret vds-pg-secret app=veba-ui
```

Edit the `function.yaml` file with the name of the container image from Step 1 if you made any changes. If not, the default VMware container image will suffice. By default, the function deployment will filter on the `DvsReconfiguredEvent` vCenter Server Event. If you wish to change this, update the `subject` field within `function.yaml` to the desired event type.


Deploy the function to the VMware Event Broker Appliance (VEBA).

```console
# deploy function

kubectl -n vmware-functions apply -f function.yaml
```

For testing purposes, the `function.yaml` contains the following annotations, which will ensure the Knative Service Pod will always run **exactly** one instance for debugging purposes. Functions deployed through through the VMware Event Broker Appliance UI defaults to scale to 0, which means the pods will only run when it is triggered by an vCenter Event.

```yaml
annotations:
autoscaling.knative.dev/maxScale: "1"
autoscaling.knative.dev/minScale: "1"
```
# Step 4 - Undeploy
```console
# undeploy function

kubectl -n vmware-functions delete -f function.yaml

# delete secret
kubectl -n vmware-functions delete secret vds-pg-secret
```
39 changes: 39 additions & 0 deletions examples/knative/powercli/kn-pcli-vds-pg-config/function.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: kn-pcli-vds-pg-config
labels:
app: veba-ui
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/maxScale: "1"
autoscaling.knative.dev/minScale: "1"
spec:
containers:
- image: us.gcr.io/daisy-284300/veba/kn-pcli-vds-pg-config:1.0
envFrom:
- secretRef:
name: vds-pg-secret
env:
- name: FUNCTION_DEBUG
value: "true"
---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
name: veba-pcli-vds-pg-config-trigger
labels:
app: veba-ui
spec:
broker: default
filter:
attributes:
type: com.vmware.event.router/event
subject: DVPortgroupReconfiguredEvent
subscriber:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: kn-pcli-vds-pg-config
128 changes: 128 additions & 0 deletions examples/knative/powercli/kn-pcli-vds-pg-config/handler.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
Function Process-Init {
[CmdletBinding()]
param()
Write-Host "$(Get-Date) - Processing Init`n"

try {
$jsonSecrets = ${env:VDS_SECRET} | ConvertFrom-Json
}
catch {
throw "`nK8s secret `$env:VDS_SECRET does not look to be defined"
}

# Extract all tag secrets for ease of use in function
$VCENTER_SERVER = ${jsonSecrets}.VCENTER_SERVER
$VCENTER_USERNAME = ${jsonSecrets}.VCENTER_USERNAME
$VCENTER_PASSWORD = ${jsonSecrets}.VCENTER_PASSWORD
$VCENTER_CERTIFICATE_ACTION = ${jsonSecrets}.VCENTER_CERTIFICATE_ACTION

# Configure TLS 1.2/1.3 support as this is required for latest vSphere release
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls13

Write-Host "$(Get-Date) - Configuring PowerCLI Configuration Settings`n"
Set-PowerCLIConfiguration -InvalidCertificateAction:${VCENTER_CERTIFICATE_ACTION} -ParticipateInCeip:$true -Confirm:$false

Write-Host "$(Get-Date) - Connecting to vCenter Server $VCENTER_SERVER`n"

try {
Connect-VIServer -Server $VCENTER_SERVER -User $VCENTER_USERNAME -Password $VCENTER_PASSWORD
}
catch {
Write-Error "$(Get-Date) - Failed to connect to vCenter Server"
throw $_
}

Write-Host "$(Get-Date) - Successfully connected to $VCENTER_SERVER`n"

Write-Host "$(Get-Date) - Init Processing Completed`n"
}

Function Process-Shutdown {
[CmdletBinding()]
param()
Write-Host "$(Get-Date) - Processing Shutdown`n"

Write-Host "$(Get-Date) - Disconnecting from vCenter Server`n"

try {
Disconnect-VIServer * -Confirm:$false
}
catch {
Write-Error "$(Get-Date) - Failed to Disconnect from vCenter Server"
}

Write-Host "$(Get-Date) - Shutdown Processing Completed`n"
}

Function Process-Handler {
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$true)][CloudNative.CloudEvents.CloudEvent]$CloudEvent
)

# Decode CloudEvent
try {
$cloudEventData = $cloudEvent | Read-CloudEventJsonData -Depth 10
}
catch {
throw "`nPayload must be JSON encoded"
}

try {
$jsonSecrets = ${env:VDS_SECRET} | ConvertFrom-Json
}
catch {
throw "`nK8s secret `$env:VDS_SECRET does not look to be defined"
}

$NOTIFY_SWITCHES = ${jsonSecrets}.NOTIFY_SWITCHES
try {
$NOTIFY_SWITCHES = [System.Convert]::ToBoolean($NOTIFY_SWITCHES)
}
catch {
Write-Host "$(Get-Date) - ERROR: Unable to convert NOTIFY_SWITCHES value to boolean"
throw $_
}

# Extract VM Name from event
$vdsName = $cloudEventData.Dvs['Name']
$pgName = $cloudEventData.Net['Name']
if(${env:FUNCTION_DEBUG} -eq "true") {
Write-Host "$(Get-Date) - DEBUG: Found VDS name $vdsName"
Write-Host "$(Get-Date) - DEBUG: Found VDS pg name $pgName"
}

try {
$vswitch = Get-VDSwitch $vdsName
}
catch {
Write-Host "$(Get-Date) - ERROR: Could not load distributed virtual switch $vdsName"
throw $_
}

try {
$pg = Get-VDPortgroup -VDSwitch $vswitch -Name $pgName
}
catch {
Write-Host "$(Get-Date) - ERROR: Could not load distributed virtual portgroup $pgName"
throw $_
}
$policy = Get-VDUplinkTeamingPolicy -VDPortgroup $pg
if ($policy.NotifySwitches -ne $NOTIFY_SWITCHES) {
Write-Host "$(Get-Date) - INFO: Setting Notify Switches to $NOTIFY_SWITCHES"
try {
Set-VDUplinkTeamingPolicy -Policy $policy -NotifySwitches $NOTIFY_SWITCHES
}
catch {
Write-Host "$(Get-Date) - ERROR: Failed to set Notify Switches value to $NOTIFY_SWITCHES"
throw $_
}
}
else {
Write-Host "$(Get-Date) - INFO: Notify Switches already set to $NOTIFY_SWITCHES, no changes made"
}

Write-Host "$(Get-Date) - VDS portgroup reconfig operation complete ...`n"

Write-Host "$(Get-Date) - Handler Processing Completed ...`n"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VDS_SECRET={"VCENTER_SERVER":"FILL-ME-IN","VCENTER_USERNAME":"FILL-ME-IN","VCENTER_PASSWORD":"FILL-ME-IN","VCENTER_CERTIFICATE_ACTION":"Ignore","NOTIFY_SWITCHES":"False"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

$headers = @{
"Content-Type" = "application/json";
"ce-specversion" = "1.0";
"ce-id" = "id-123";
"ce-source" = "source-123";
"ce-type" = "com.vmware.event.router/event";
"ce-subject" = "DvsReconfiguredEvent";
}

$body = Get-Content -Raw -Path "./test-payload.json"

Write-Host "Testing Function ..."
Invoke-WebRequest -Uri http://localhost:8080 -Method POST -Headers $headers -Body $body

Write-host "See docker container console for output"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

echo "Testing Function ..."
curl -d@test-payload.json \
-H "Content-Type: application/json" \
-H 'ce-specversion: 1.0' \
-H 'ce-id: d70079f9-fddd-4b7f-aa76-1193f28b0611' \
-H 'ce-source: https://vcenter.local/sdk' \
-H 'ce-type: com.vmware.event.router/event' \
-H 'ce-subject: DvsReconfiguredEvent' \
-X POST localhost:8080

echo "See docker container console for output"
Loading

0 comments on commit fdf5c64

Please sign in to comment.