Repository: vmware/govmomi
Pull request #2435 (2021-05-11 10:42:56)

govc: Add support for getting the VC proxy and no-proxy configuration
govc: Add support for getting the VC proxy and no-proxy configuration
Introduced the vAPI bindings and the implementation for those

govc output:
```
HTTP proxy:          Enabled
                     Server:    http://10.186.17.218
                     Port:      80
                     Username:  user
HTTPS proxy:         Disabled
FTP proxy:           Enabled
                     Server:  http://google.com
                     Port:    21
No Proxy addresses:  localhost, 127.0.0.1
```


Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..88ca0540
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,30 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[BUG] "
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+
+1. 
+1.
+1.
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Affected version**
+Please provide details on the version used, e.g. release tag, commit, module version, etc.
+
+**Screenshots/Debug Output**
+If applicable, add screenshots or debug output to help explain your problem.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..f8df48bb
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: 'kind/feature'
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I cannot do X because [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..93f06de1
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,41 @@
+## Description
+
+Please include a summary of the change and which issue is fixed. Please also
+include relevant motivation and context. List any dependencies that are required
+for this change.
+
+Closes: #(issue-number)
+
+## Type of change
+
+Please mark options that are relevant:
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to
+  not work as expected)
+- [ ] This change requires a documentation update
+
+## How Has This Been Tested?
+
+Please describe the tests that you ran to verify your changes. Provide
+instructions so we can reproduce. If applicable, please also list any relevant
+details for your test configuration.
+
+- [ ] Test Description 1
+- [ ] Test Description 2
+
+**Test Configuration**:
+* Toolchain:
+* SDK:
+* (add more if needed)
+
+## Checklist:
+
+- [ ] My code follows the CONTRIBUTION [guidelines](./../CONTRIBUTING.md) of
+  this project
+- [ ] I have commented my code, particularly in hard-to-understand areas
+- [ ] I have made corresponding changes to the documentation
+- [ ] I have added tests that prove my fix is effective or that my feature works
+- [ ] New and existing unit tests pass locally with my changes
+- [ ] Any dependent changes have been merged
\ No newline at end of file
diff --git a/govc/main.go b/govc/main.go
index ff6f66c6..6e028cc4 100644
--- a/govc/main.go
+++ b/govc/main.go
@@ -88,6 +88,7 @@ import (
 	_ "github.com/vmware/govmomi/govc/task"
 	_ "github.com/vmware/govmomi/govc/vapp"
 	_ "github.com/vmware/govmomi/govc/vcsa/log"
+	_ "github.com/vmware/govmomi/govc/vcsa/proxy"
 	_ "github.com/vmware/govmomi/govc/version"
 	_ "github.com/vmware/govmomi/govc/vm"
 	_ "github.com/vmware/govmomi/govc/vm/disk"
diff --git a/govc/vcsa/proxy/proxy.go b/govc/vcsa/proxy/proxy.go
new file mode 100644
index 00000000..a142bf83
--- /dev/null
+++ b/govc/vcsa/proxy/proxy.go
@@ -0,0 +1,116 @@
+/*
+Copyright (c) 2021 VMware, Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package net
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"io"
+	"strings"
+	"text/tabwriter"
+
+	"github.com/vmware/govmomi/govc/cli"
+	"github.com/vmware/govmomi/govc/flags"
+	vnetworking "github.com/vmware/govmomi/vapi/appliance/networking"
+)
+
+type info struct {
+	*flags.ClientFlag
+	*flags.OutputFlag
+}
+
+func init() {
+	cli.Register("vcsa.net.proxy.info", &info{})
+}
+
+func (cmd *info) Register(ctx context.Context, f *flag.FlagSet) {
+	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
+	cmd.ClientFlag.Register(ctx, f)
+
+	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
+	cmd.OutputFlag.Register(ctx, f)
+}
+
+func (cmd *info) Process(ctx context.Context) error {
+	if err := cmd.ClientFlag.Process(ctx); err != nil {
+		return err
+	}
+	if err := cmd.OutputFlag.Process(ctx); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (cmd *info) Description() string {
+	return `Retrieve the VC networking proxy configuration
+
+Examples:
+  govc vcsa.net.proxy.info`
+}
+
+type proxyResult struct {
+	proxy   *vnetworking.ProxyList
+	noProxy []string
+}
+
+func (cmd *info) Run(ctx context.Context, f *flag.FlagSet) error {
+	c, err := cmd.RestClient()
+	if err != nil {
+		return err
+	}
+
+	fwd := vnetworking.NewManager(c)
+
+	proxyRes, err := fwd.ProxyList(ctx)
+	if err != nil {
+		fmt.Println(err)
+		return nil
+	}
+
+	noProxyRes, err := fwd.NoProxy(ctx)
+	if err != nil {
+		fmt.Println(err)
+		return nil
+	}
+
+	return cmd.WriteResult(&proxyResult{proxyRes, noProxyRes})
+}
+
+func (res proxyResult) Write(w io.Writer) error {
+	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
+	printProxyConfig("HTTP", res.proxy.Http, tw)
+	printProxyConfig("HTTPS", res.proxy.Https, tw)
+	printProxyConfig("FTP", res.proxy.Ftp, tw)
+	fmt.Fprintf(tw, "No Proxy addresses:\t%s\n", strings.Join(res.noProxy, ", "))
+
+	return tw.Flush()
+}
+
+func printProxyConfig(proxyName string, proxyProtocolConfig vnetworking.Proxy, w io.Writer) {
+	if !proxyProtocolConfig.Enabled {
+		fmt.Fprintf(w, "%s proxy:\tDisabled\n", proxyName)
+		return
+	}
+
+	fmt.Fprintf(w, "%s proxy:\tEnabled\n", proxyName)
+	fmt.Fprintf(w, "\tServer:\t%s\n", proxyProtocolConfig.Server)
+	fmt.Fprintf(w, "\tPort:\t%d\n", proxyProtocolConfig.Port)
+	if proxyProtocolConfig.Username != "" {
+		fmt.Fprintf(w, "\tUsername:\t%s\n", proxyProtocolConfig.Username)
+	}
+}
diff --git a/simulator/simulator.go b/simulator/simulator.go
index f8f8e524..b26451ae 100644
--- a/simulator/simulator.go
+++ b/simulator/simulator.go
@@ -335,32 +335,34 @@ func (s *Service) About(w http.ResponseWriter, r *http.Request) {
 
 	f := reflect.TypeOf((*soap.HasFault)(nil)).Elem()
 
-	for _, obj := range Map.objects {
-		kind := obj.Reference().Type
-		if seen[kind] {
-			continue
-		}
-		seen[kind] = true
-
-		about.Types = append(about.Types, kind)
-
-		t := reflect.TypeOf(obj)
-		for i := 0; i < t.NumMethod(); i++ {
-			m := t.Method(i)
-			if seen[m.Name] {
+	for _, sdk := range s.sdk {
+		for _, obj := range sdk.objects {
+			kind := obj.Reference().Type
+			if seen[kind] {
 				continue
 			}
-			seen[m.Name] = true
+			seen[kind] = true
 
-			in := m.Type.NumIn()
-			if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
-				continue
-			}
-			if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
-				continue
-			}
+			about.Types = append(about.Types, kind)
+
+			t := reflect.TypeOf(obj)
+			for i := 0; i < t.NumMethod(); i++ {
+				m := t.Method(i)
+				if seen[m.Name] {
+					continue
+				}
+				seen[m.Name] = true
 
-			about.Methods = append(about.Methods, strings.Replace(m.Name, "Task", "_Task", 1))
+				in := m.Type.NumIn()
+				if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
+					continue
+				}
+				if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
+					continue
+				}
+
+				about.Methods = append(about.Methods, strings.Replace(m.Name, "Task", "_Task", 1))
+			}
 		}
 	}
 
diff --git a/vapi/appliance/networking/proxy.go b/vapi/appliance/networking/proxy.go
new file mode 100644
index 00000000..556e1e76
--- /dev/null
+++ b/vapi/appliance/networking/proxy.go
@@ -0,0 +1,90 @@
+/*
+Copyright (c) 2021 VMware, Inc. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0.
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package networking
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/vmware/govmomi/vapi/rest"
+)
+
+const applianceProxyConfigPath = "/appliance/networking/proxy"
+const applianceNoProxyConfigPath = "/appliance/networking/noproxy"
+
+// Manager provides convenience methods to configure appliance proxy.
+type Manager struct {
+	*rest.Client
+}
+
+// NewManager creates a new Manager with the given client
+func NewManager(client *rest.Client) *Manager {
+	return &Manager{
+		Client: client,
+	}
+}
+
+// Proxy represents configuration for specific proxy - ftp, http, https.
+type Proxy struct {
+	Server   string `json:"server,omitempty"`
+	Port     int    `json:"port,omitempty"`
+	Username string `json:"username,omitempty"`
+	Password string `json:"password,omitempty"`
+	Enabled  bool   `json:"enabled,omitempty"`
+}
+
+// ProxyList represents configuration for vcenter proxy.
+type ProxyList struct {
+	Ftp   Proxy `json:"ftp,omitempty"`
+	Http  Proxy `json:"http,omitempty"`
+	Https Proxy `json:"https,omitempty"`
+}
+
+// ProxyList returns all Proxy configuration.
+func (m *Manager) ProxyList(ctx context.Context) (*ProxyList, error) {
+	var res ProxyList
+	var rawRes []struct {
+		Key   string
+		Value Proxy
+	}
+
+	r := m.Resource(applianceProxyConfigPath)
+	err := m.Do(ctx, r.Request(http.MethodGet), &rawRes)
+	if err != nil {
+		return &res, err
+	}
+
+	for _, c := range rawRes {
+		switch c.Key {
+		case "http":
+			res.Http = c.Value
+		case "https":
+			res.Https = c.Value
+		case "ftp":
+			res.Ftp = c.Value
+		}
+	}
+
+	return &res, nil
+}
+
+// NoProxy returns all excluded servers for proxying.
+func (m *Manager) NoProxy(ctx context.Context) ([]string, error) {
+	r := m.Resource(applianceNoProxyConfigPath)
+	var res []string
+	return res, m.Do(ctx, r.Request(http.MethodGet), &res)
+}


Test patch:
diff --git a/govc/test/vcsim.bats b/govc/test/vcsim.bats
index 4da85e11..879c1cc8 100755
--- a/govc/test/vcsim.bats
+++ b/govc/test/vcsim.bats
@@ -48,6 +48,8 @@ EOF
   run curl -skf "$url/about"
   assert_matches "CurrentTime" # 1 param (without Context)
   assert_matches "TerminateSession" # 2 params (with Context)
+  assert_matches "CnsAttachVolume" # method from namespace vsan
+  assert_matches "PbmCreate" # method from namespace pbm
 
   run curl -skf "$url/debug/vars"
   assert_success