From 95a48a89a6ffd4851a2b78df80471282b5482e90 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Thu, 28 Sep 2023 11:55:15 +1000 Subject: [PATCH] test build --- go.mod | 2 + internal/exporters/UcsmTemperatures.go | 28 +- local/go-ucs/.gitignore | 30 + local/go-ucs/Gopkg.lock | 21 + local/go-ucs/Gopkg.toml | 34 + local/go-ucs/LICENSE | 24 + local/go-ucs/README.md | 27 + local/go-ucs/api/api.go | 326 ++++++++ local/go-ucs/api/client.go | 341 ++++++++ local/go-ucs/api/doc.go | 2 + .../go-ucs/api/example_aaa_keepalive_test.go | 73 ++ local/go-ucs/api/example_aaa_login_test.go | 44 + local/go-ucs/api/example_aaa_refresh_test.go | 51 ++ .../api/example_composite_filter_test.go | 96 +++ .../api/example_config_resolve_class_test.go | 73 ++ .../example_config_resolve_classes_test.go | 81 ++ .../api/example_config_resolve_dn_test.go | 63 ++ .../api/example_config_resolve_dns_test.go | 80 ++ local/go-ucs/api/example_rate_limiter_test.go | 88 ++ local/go-ucs/api/xml.go | 54 ++ local/go-ucs/api/xml_test.go | 64 ++ local/go-ucs/go.mod | 11 + local/go-ucs/mo/doc.go | 2 + local/go-ucs/mo/mo.go | 750 ++++++++++++++++++ local/go-ucs/version/version.go | 5 + 25 files changed, 2358 insertions(+), 12 deletions(-) create mode 100644 local/go-ucs/.gitignore create mode 100755 local/go-ucs/Gopkg.lock create mode 100755 local/go-ucs/Gopkg.toml create mode 100755 local/go-ucs/LICENSE create mode 100755 local/go-ucs/README.md create mode 100755 local/go-ucs/api/api.go create mode 100755 local/go-ucs/api/client.go create mode 100755 local/go-ucs/api/doc.go create mode 100755 local/go-ucs/api/example_aaa_keepalive_test.go create mode 100755 local/go-ucs/api/example_aaa_login_test.go create mode 100755 local/go-ucs/api/example_aaa_refresh_test.go create mode 100755 local/go-ucs/api/example_composite_filter_test.go create mode 100755 local/go-ucs/api/example_config_resolve_class_test.go create mode 100755 local/go-ucs/api/example_config_resolve_classes_test.go create mode 100755 local/go-ucs/api/example_config_resolve_dn_test.go create mode 100755 local/go-ucs/api/example_config_resolve_dns_test.go create mode 100755 local/go-ucs/api/example_rate_limiter_test.go create mode 100755 local/go-ucs/api/xml.go create mode 100755 local/go-ucs/api/xml_test.go create mode 100755 local/go-ucs/go.mod create mode 100755 local/go-ucs/mo/doc.go create mode 100755 local/go-ucs/mo/mo.go create mode 100755 local/go-ucs/version/version.go diff --git a/go.mod b/go.mod index c011e07..c0fa8e5 100644 --- a/go.mod +++ b/go.mod @@ -19,3 +19,5 @@ require ( golang.org/x/time v0.3.0 // indirect google.golang.org/protobuf v1.30.0 // indirect ) + +replace github.com/dnaeon/go-ucs => ./local/go-ucs \ No newline at end of file diff --git a/internal/exporters/UcsmTemperatures.go b/internal/exporters/UcsmTemperatures.go index a16277c..ac04c31 100644 --- a/internal/exporters/UcsmTemperatures.go +++ b/internal/exporters/UcsmTemperatures.go @@ -15,13 +15,14 @@ type UcsmTemperaturesCollector struct { ctx context.Context } -type ComputeMbTempStats struct { - XMLName xml.Name `xml:"computeMbTempStats"` - Dn string `xml:"dn,attr,omitempty"` - FmTempSenIo string `xml:"fmTempSenIo,attr,omitempty"` - FmTempSenRear string `xml:"fmTempSenRear,attr,omitempty"` -} - +/* + type ComputeMbTempStats struct { + XMLName xml.Name `xml:"computeMbTempStats"` + Dn string `xml:"dn,attr,omitempty"` + FmTempSenIo string `xml:"fmTempSenIo,attr,omitempty"` + FmTempSenRear string `xml:"fmTempSenRear,attr,omitempty"` + } +*/ func NewUcsmTemperatureCollector(client *api.Client, ctx context.Context) *UcsmTemperaturesCollector { return &UcsmTemperaturesCollector{ ucsClient: client, @@ -52,7 +53,7 @@ func (u *UcsmTemperaturesCollector) Collect(ch chan<- prometheus.Metric) { type temps struct { XMLName xml.Name - Temperatures []ComputeMbTempStats `xml:"computeMbTempStats"` + Temperatures []mo.ComputeMbTempStats `xml:"computeMbTempStats"` } bladeRequest := api.ConfigResolveClassRequest{ @@ -74,6 +75,7 @@ func (u *UcsmTemperaturesCollector) Collect(ch chan<- prometheus.Metric) { log.Printf("%s:\n", blade.Dn) boardDn := "^" + blade.Dn + "/board/" + //boardDn := blade.Dn + "/board/" log.Printf("%s:\n", boardDn) log.Printf("\tNumber of CPUs: %d\n", blade.NumOfCpus) @@ -100,10 +102,12 @@ func (u *UcsmTemperaturesCollector) Collect(ch chan<- prometheus.Metric) { var tempList temps log.Printf("Retrieving temperatures for this blade\n") - if err := u.ucsClient.ConfigResolveClass(u.ctx, tempReq, &tempList); err != nil { - log.Fatalf("Unable to retrieve `computeMbTempStats` managed object: %s", err) - } - + /* + if err := u.ucsClient.ConfigResolveClass(u.ctx, tempReq, &tempList); err != nil { + log.Fatalf("Unable to retrieve `computeMbTempStats` managed object: %s", err) + } + */ + u.ucsClient.ConfigResolveClass(u.ctx, tempReq, &tempList) log.Printf("Front Temperature: %v\n", tempList) } diff --git a/local/go-ucs/.gitignore b/local/go-ucs/.gitignore new file mode 100644 index 0000000..39b8783 --- /dev/null +++ b/local/go-ucs/.gitignore @@ -0,0 +1,30 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# Ignore Emacs backup files +*~ + +# Vim swap files +*.swp diff --git a/local/go-ucs/Gopkg.lock b/local/go-ucs/Gopkg.lock new file mode 100755 index 0000000..1b7165e --- /dev/null +++ b/local/go-ucs/Gopkg.lock @@ -0,0 +1,21 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = ["context"] + revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23" + +[[projects]] + branch = "master" + name = "golang.org/x/time" + packages = ["rate"] + revision = "fbb02b2291d28baffd63558aa44b4b56f178d650" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "95c0627ec50dce844f8d1485f7dad5ed1d321aaae8cd00bb303cc7709e89fd4d" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/local/go-ucs/Gopkg.toml b/local/go-ucs/Gopkg.toml new file mode 100755 index 0000000..3a2c82f --- /dev/null +++ b/local/go-ucs/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "master" + name = "golang.org/x/time" + +[prune] + go-tests = true + unused-packages = true diff --git a/local/go-ucs/LICENSE b/local/go-ucs/LICENSE new file mode 100755 index 0000000..bb05397 --- /dev/null +++ b/local/go-ucs/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2017-2018 Marin Atanasov Nikolov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer + in this position and unchanged. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/local/go-ucs/README.md b/local/go-ucs/README.md new file mode 100755 index 0000000..7f3074c --- /dev/null +++ b/local/go-ucs/README.md @@ -0,0 +1,27 @@ +## go-ucs: Go library for the Cisco UCS API + +[![GoDoc](https://godoc.org/github.com/dnaeon/go-ucs?status.svg)](https://godoc.org/github.com/dnaeon/go-ucs) + +`go-ucs` is a Go library for interfacing with the Cisco UCS API. + +## Documentation + +The API documentation is available [here](https://godoc.org/github.com/dnaeon/go-ucs). + +## Installation + +In order to install `go-ucs` execute the following command. + +``` +go get -v github.com/dnaeon/go-ucs +``` + +## Tests + +```bash +go test -v ./... +``` + +## Examples + +Check the included examples from this repository. diff --git a/local/go-ucs/api/api.go b/local/go-ucs/api/api.go new file mode 100755 index 0000000..ef6ebc3 --- /dev/null +++ b/local/go-ucs/api/api.go @@ -0,0 +1,326 @@ +package api + +import ( + "encoding/xml" + "errors" + "fmt" + + "github.com/dnaeon/go-ucs/version" +) + +// The remote API endpoint to which we POST requests. +const apiEndpoint = "nuova" + +// The UserAgent that we use for our requests. +const userAgent = "go-ucs/" + version.Version + +// BaseResponse contains the base attributes as returned in a response from a +// Cisco UCS API endpoint. +type BaseResponse struct { + Cookie string `xml:"cookie,attr"` + Response string `xml:"response,attr"` + ErrorCode string `xml:"errorCode,attr,omitempty"` + InvocationResult string `xml:"invocationResult,attr,omitempty"` + ErrorDescription string `xml:"errorDescr,attr,omitempty"` +} + +// IsError returns a boolean indicating whether the response contains errors. +func (b *BaseResponse) IsError() bool { + return b.ErrorCode != "" +} + +// Error implements the error interface. +func (b *BaseResponse) Error() string { + return fmt.Sprintf("%s: %s (code %s)", b.ErrorDescription, b.InvocationResult, b.ErrorCode) +} + +// ToError creates a new error.Error from the error response fields. +func (b *BaseResponse) ToError() error { + return errors.New(b.Error()) +} + +// AaaLoginRequest is the type which is sent during initial login +// in order to obtain authentication cookie. +type AaaLoginRequest struct { + XMLName xml.Name `xml:"aaaLogin"` + InName string `xml:"inName,attr"` + InPassword string `xml:"inPassword,attr"` +} + +// AaaLoginResponse represents the response after a successful login to UCS manager. +type AaaLoginResponse struct { + BaseResponse + XMLName xml.Name `xml:"aaaLogin"` + OutCookie string `xml:"outCookie,attr,omitempty"` + OutRefreshPeriod int `xml:"outRefreshPeriod,attr,omitempty"` + OutPriv string `xml:"outPriv,attr,omitempty"` + OutDomains string `xml:"outDomains,attr,omitempty"` + OutChannel string `xml:"outChannel,attr,omitempty"` + OutEvtChannel string `xml:"outEvtChannel,attr,omitempty"` + OutName string `xml:"outName,attr,omitempty"` + OutVersion string `xml:"outVersion,attr,omitempty"` + OutSessionId string `xml:"outSessionId,attr,omitempty"` +} + +// AaaRefreshRequest type is used for sending a request to the remote API endpoint for +// refreshing a session using the 47-character cookie obtained from a previous refresh +// or during the initial authentication as returned in a AaaLoginResponse. +type AaaRefreshRequest struct { + XMLName xml.Name `xml:"aaaRefresh"` + InName string `xml:"inName,attr"` + InPassword string `xml:"inPassword,attr"` + InCookie string `xml:"inCookie,attr"` +} + +// AaaRefreshResponse is the response associated to a AaaRefreshRequest. +type AaaRefreshResponse struct { + XMLName xml.Name `xml:"aaaRefresh"` + AaaLoginResponse +} + +// AaaLogoutRequest type is used for sending a request to invalidate an existing +// authentication cookie. +type AaaLogoutRequest struct { + XMLName xml.Name `xml:"aaaLogout"` + InCookie string `xml:"inCookie,attr"` +} + +// AaaLogoutResponse represents the type that is returned after a call to aaaLogout method. +type AaaLogoutResponse struct { + BaseResponse + XMLName xml.Name `xml:"aaaLogout"` + OutStatus string `xml:"outStatus,attr,omitempty"` +} + +// AaaKeepAliveRequest type is used for sending a request for keeping a session active +// until the default session time expires. +type AaaKeepAliveRequest struct { + XMLName xml.Name `xml:"aaaKeepAlive"` + Cookie string `xml:"cookie,attr"` +} + +// AaaKeepAliveResponse is the response type associated with a AaaKeepAliveRequest. +type AaaKeepAliveResponse struct { + BaseResponse + XMLName xml.Name `xml:"aaaKeepAlive"` + Cookie string `xml:"cookie,attr"` +} + +// ConfigResolveDnRequest type is used for constructing requests that retrieve a +// single managed object with the given DN. +type ConfigResolveDnRequest struct { + XMLName xml.Name `xml:"configResolveDn"` + Cookie string `xml:"cookie,attr"` + Dn string `xml:"dn,attr"` + InHierarchical string `xml:"inHierarchical,attr,omitempty"` +} + +// ConfigResolveDnResponse is the type associated with a ConfigResolveDnRequest type. +// Specific classes contained within OutConfig should be xml.Unmarshal'ed first. +type ConfigResolveDnResponse struct { + BaseResponse + XMLName xml.Name `xml:"configResolveDn"` + Dn string `xml:"dn,attr"` + OutConfig InnerXml `xml:"outConfig"` +} + +// Dn represents a single managed object DN. +type Dn struct { + XMLName xml.Name `xml:"dn"` + Value string `xml:"value,attr"` +} + +// NewDn creates a new DN value. +func NewDn(value string) Dn { + dn := Dn{ + Value: value, + } + + return dn +} + +// ConfigResolveDnsRequest type is used for constructing requests that retrieve +// managed objects for a list of given DNs. +type ConfigResolveDnsRequest struct { + XMLName xml.Name `xml:"configResolveDns"` + Cookie string `xml:"cookie,attr"` + InHierarchical string `xml:"inHierarchical,attr,omitempty"` + InDns []Dn `xml:"inDns>dn"` +} + +// ConfigResolveDnsResponse is the response type associated with a ConfigResolveDnsRequest. +// The managed objects within OutConfigs field should be xml.Unmarshal'ed. +type ConfigResolveDnsResponse struct { + BaseResponse + XMLName xml.Name `xml:"configResolveDns"` + OutUnresolved []Dn `xml:"outUnresolved>dn"` + OutConfigs InnerXml `xml:"outConfigs"` +} + +// InnerXml represents a generic configuration retrieved by the various query methods. +// After a successful result from a query method a client should unmarshal the data +// contained within an InnerXml to the specific managed object. +type InnerXml struct { + XMLName xml.Name + Inner []byte `xml:",innerxml"` +} + +// ConfigResolveClassRequest type is used for constructing requests that retrieve +// managed objects of a given class. +type ConfigResolveClassRequest struct { + XMLName xml.Name `xml:"configResolveClass"` + Cookie string `xml:"cookie,attr"` + ClassId string `xml:"classId,attr"` + InHierarchical string `xml:"inHierarchical,attr,omitempty"` + InFilter FilterAny `xml:"inFilter>any,omitempty"` +} + +// ConfigResolveClassResponse is the type associated with a ConfigResolveClassRequest. +// Specific classes contained within OutConfigs should be xml.Unmarshal'ed first. +type ConfigResolveClassResponse struct { + BaseResponse + XMLName xml.Name `xml:"configResolveClass"` + OutConfigs InnerXml `xml:"outConfigs"` +} + +// Id represents an ID of a class. +type Id struct { + XMLName xml.Name `xml:"Id"` + Value string `xml:"value,attr"` +} + +// NewId creates a new class id. +func NewId(value string) Id { + id := Id{ + Value: value, + } + + return id +} + +// ConfigResolveClassesRequest type is used for constructing requests that retrieve managed +// objects in several classes. +type ConfigResolveClassesRequest struct { + XMLName xml.Name `xml:"configResolveClasses"` + Cookie string `xml:"cookie,attr"` + InHierarchical string `xml:"inHierarchical,attr,omitempty"` + InIds []Id `xml:"inIds>Id"` +} + +// ConfigResolveClassesResponse is the response type associated with a ConfigResolveClassesRequest. +type ConfigResolveClassesResponse struct { + BaseResponse + XMLName xml.Name `xml:"configResolveClasses"` + OutConfigs InnerXml `xml:"outConfigs"` +} + +// ConfigResolveChildren type is used for constructing requests that retrieve +// children of managed objects under a specified DN. A filter can be used to +// reduce the number of children being returned. +type ConfigResolveChildrenRequest struct { + XMLName xml.Name `xml:"configResolveChildren"` + Cookie string `xml:"cookie,attr"` + ClassId string `xml:"classId,attr"` + InDn string `xml:"inDn,attr"` + InHierarchical string `xml:"inHierarchical,attr"` + InFilter FilterAny `xml:"inFilter>any,omitempty"` +} + +// ConfigResolveChildrenResponse is the response type associated with a ConfigResolveChildrenRequest. +type ConfigResolveChildrenResponse struct { + BaseResponse + XMLName xml.Name `xml:"configResolveChildren"` + OutConfigs InnerXml `xml:"outConfigs"` +} + +// FilterAny represents any valid filter. +type FilterAny interface{} + +// FilterProperty represents a Property Filter. +type FilterProperty struct { + Class string `xml:"class,attr"` + Property string `xml:"property,attr"` + Value string `xml:"value,attr"` +} + +// FilterEq represents an Equality Filter. +type FilterEq struct { + XMLName xml.Name `xml:"eq"` + FilterProperty +} + +// FilterNe represents a Not Equal Filter. +type FilterNe struct { + XMLName xml.Name `xml:"ne"` + FilterProperty +} + +// FilterGt represents a Greater Than Filter. +type FilterGt struct { + XMLName xml.Name `xml:"gt"` + FilterProperty +} + +// FilterGe represents a Greater Than Or Equal To Filter. +type FilterGe struct { + XMLName xml.Name `xml:"ge"` + FilterProperty +} + +// FilterLt represents a Less Than Filter. +type FilterLt struct { + XMLName xml.Name `xml:"lt"` + FilterProperty +} + +// FilterLe represents a Less Than Or Equal To Filter. +type FilterLe struct { + XMLName xml.Name `xml:"le"` + FilterProperty +} + +// FilterWildcard represents a Wildcard Filter. +// The wildcard filter uses standard regular expression syntax. +type FilterWildcard struct { + XMLName xml.Name `xml:"wcard"` + FilterProperty +} + +// FilterAnyBits represents an Any Bits Filter. +type FilterAnyBits struct { + XMLName xml.Name `xml:"anybit"` + FilterProperty +} + +// FilterAllBits represents an All Bits Filter. +type FilterAllBits struct { + XMLName xml.Name `xml:"allbits"` + FilterProperty +} + +// FilterAnd represents a composite AND Filter. +type FilterAnd struct { + XMLName xml.Name `xml:"and"` + Filters []FilterAny +} + +// FilterOr represents a composite OR Filter. +type FilterOr struct { + XMLName xml.Name `xml:"or"` + Filters []FilterAny +} + +// FilterNot represents a NOT Modifier Filter. +type FilterNot struct { + XMLName xml.Name `xml:"not"` + Filters []FilterAny +} + +// FilterBetween represents a Between Filter. +type FilterBetween struct { + XMLName xml.Name `xml:"bw"` + Class string `xml:"class,attr"` + Property string `xml:"property,attr"` + FirstVault string `xml:"firstValue,attr"` + SecondValue string `xml:"secondValue,attr"` +} diff --git a/local/go-ucs/api/client.go b/local/go-ucs/api/client.go new file mode 100755 index 0000000..1a415ef --- /dev/null +++ b/local/go-ucs/api/client.go @@ -0,0 +1,341 @@ +package api + +import ( + "bytes" + "context" + "encoding/xml" + "fmt" + "io" + "net/http" + "net/url" + "time" + + "github.com/dnaeon/go-ucs/mo" + "golang.org/x/time/rate" +) + +// RateLimit limits the number of requests per second that a Client +// can send to a remote Cisco UCS API endpoint using a rate.Limiter +// token bucket configured with the provided requests per seconds and +// burst. A request will wait for up to the given wait time. +type RateLimit struct { + // RequestsPerSecond defines the allowed number of requests per second. + RequestsPerSecond float64 + + // Burst is the maximum burst size. + Burst int + + // Wait defines the maximum time a request will wait for a token to be consumed. + Wait time.Duration +} + +// Config type contains the setting used by the Client. +type Config struct { + // HttpClient is the HTTP client to use for sending requests. + // If nil then we use http.DefaultClient for all requests. + HttpClient *http.Client + + // Endpoint is the base URL to the remote Cisco UCS Manager endpoint. + Endpoint string + + // Username to use when authenticating to the remote endpoint. + Username string + + // Password to use when authenticating to the remote endpoint. + Password string + + // RateLimit is used for limiting the number of requests per second + // against the remote Cisco UCS API endpoint using a token bucket. + RateLimit *RateLimit +} + +// Client is used for interfacing with the remote Cisco UCS API endpoint. +type Client struct { + config *Config + apiUrl *url.URL + limiter *rate.Limiter + + // Cookie is the authentication cookie currently in use. + // It's value is set by the AaaLogin and AaaRefresh methods. + Cookie string +} + +// NewClient creates a new API client from the given config. +func NewClient(config Config) (*Client, error) { + if config.HttpClient == nil { + config.HttpClient = http.DefaultClient + } + + baseUrl, err := url.Parse(config.Endpoint) + if err != nil { + return nil, err + } + + apiUrl, err := url.Parse(apiEndpoint) + if err != nil { + return nil, err + } + + var limiter *rate.Limiter + if config.RateLimit != nil { + rps := rate.Limit(config.RateLimit.RequestsPerSecond) + limiter = rate.NewLimiter(rps, config.RateLimit.Burst) + } + + client := &Client{ + config: &config, + apiUrl: baseUrl.ResolveReference(apiUrl), + limiter: limiter, + } + + return client, nil +} + +// Hostname returns the host portion of the remote UCS API endpoint without any port number. +func (c *Client) Hostname() string { + return c.apiUrl.Host +} + +// AaaLogin performs the initial authentication to the remote Cisco UCS API endpoint. +func (c *Client) AaaLogin(ctx context.Context) (*AaaLoginResponse, error) { + req := AaaLoginRequest{ + InName: c.config.Username, + InPassword: c.config.Password, + } + + var resp AaaLoginResponse + if err := c.Request(ctx, req, &resp); err != nil { + return nil, err + } + + if resp.IsError() { + return nil, resp.ToError() + } + + // Set authentication cookie for future re-use when needed. + c.Cookie = resp.OutCookie + + return &resp, nil +} + +// AaaRefresh refreshes the current session by requesting a new authentication cookie. +func (c *Client) AaaRefresh(ctx context.Context) (*AaaRefreshResponse, error) { + req := AaaRefreshRequest{ + InName: c.config.Username, + InPassword: c.config.Password, + InCookie: c.Cookie, + } + + var resp AaaRefreshResponse + if err := c.Request(ctx, req, &resp); err != nil { + return nil, err + } + + if resp.IsError() { + return nil, resp.ToError() + } + + // Set new authentication cookie + c.Cookie = resp.OutCookie + + return &resp, nil +} + +// AaaKeepAlive sends a request to keep the current session active using the same cookie. +func (c *Client) AaaKeepAlive(ctx context.Context) (*AaaKeepAliveResponse, error) { + req := AaaKeepAliveRequest{ + Cookie: c.Cookie, + } + + var resp AaaKeepAliveResponse + if err := c.Request(ctx, req, &resp); err != nil { + return nil, err + } + + if resp.IsError() { + return nil, resp.ToError() + } + + return &resp, nil +} + +// AaaLogout invalidates the current client session. +func (c *Client) AaaLogout(ctx context.Context) (*AaaLogoutResponse, error) { + req := AaaLogoutRequest{ + InCookie: c.Cookie, + } + + var resp AaaLogoutResponse + if err := c.Request(ctx, req, &resp); err != nil { + return nil, err + } + + if resp.IsError() { + return nil, resp.ToError() + } + + c.Cookie = "" + + return &resp, nil +} + +// doRequest sends a request to the remote Cisco UCS API endpoint. +func (c *Client) doRequest(ctx context.Context, in, out interface{}) error { + data, err := xmlMarshalWithSelfClosingTags(in) + if err != nil { + return err + } + + fmt.Println("doRequest sending following XML request:") + fmt.Println(string(data[:])) + + r, err := http.NewRequest("POST", c.apiUrl.String(), bytes.NewBuffer(data)) + if err != nil { + return err + } + + req := r.WithContext(ctx) + req.Header.Set("User-Agent", userAgent) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := c.config.HttpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + fmt.Println("doRequest received following response:") + fmt.Println(string(body[:])) + + return xml.Unmarshal(body, &out) +} + +// Request sends a POST request to the remote Cisco UCS API endpoint. +func (c *Client) Request(ctx context.Context, in, out interface{}) error { + // Rate limit requests if we are using a limiter + if c.limiter != nil { + ctxWithTimeout, cancel := context.WithTimeout(ctx, c.config.RateLimit.Wait) + defer cancel() + if err := c.limiter.Wait(ctxWithTimeout); err != nil { + return err + } + } + + return c.doRequest(ctx, in, out) +} + +// RequestNow sends a POST request to the remote Cisco UCS API endpoint immediately. +// This bypasses any rate limiter configuration that may be used and is +// meant to be used for priority requests, e.g. refreshing a token, logging out, etc. +func (c *Client) RequestNow(ctx context.Context, in, out interface{}) error { + return c.doRequest(ctx, in, out) +} + +// ConfigResolveDn retrieves a single managed object for a specified DN. +func (c *Client) ConfigResolveDn(ctx context.Context, in ConfigResolveDnRequest, out mo.Any) error { + var resp ConfigResolveDnResponse + if err := c.Request(ctx, in, &resp); err != nil { + return err + } + + if resp.IsError() { + return resp.ToError() + } + + // The requested managed object is contained within the inner XML document, + // which we need to unmarshal first into the given concrete type. + return xml.Unmarshal(resp.OutConfig.Inner, &out) + +} + +// ConfigResolveDns retrieves managed objects for a specified list of DNs. +func (c *Client) ConfigResolveDns(ctx context.Context, in ConfigResolveDnsRequest, out mo.Any) (*ConfigResolveDnsResponse, error) { + var resp ConfigResolveDnsResponse + if err := c.Request(ctx, in, &resp); err != nil { + return nil, err + } + + if resp.IsError() { + return nil, resp.ToError() + } + + inner, err := xml.Marshal(resp.OutConfigs) + if err != nil { + return nil, err + } + + if err := xml.Unmarshal(inner, &out); err != nil { + return nil, err + } + + return &resp, nil +} + +// ConfigResolveClass retrieves managed objects of the specified class. +func (c *Client) ConfigResolveClass(ctx context.Context, in ConfigResolveClassRequest, out mo.Any) error { + var resp ConfigResolveClassResponse + if err := c.Request(ctx, in, &resp); err != nil { + return err + } + + if resp.IsError() { + return resp.ToError() + } + + inner, err := xml.Marshal(resp.OutConfigs) + if err != nil { + return err + } + + // The requested managed objects are contained within the inner XML document, + // which we need to unmarshal first into the given concrete type. + return xml.Unmarshal(inner, &out) +} + +// ConfigResolveClasses retrieves managed objects from the specified list of classes. +func (c *Client) ConfigResolveClasses(ctx context.Context, in ConfigResolveClassesRequest, out mo.Any) error { + var resp ConfigResolveClassesResponse + if err := c.Request(ctx, in, &resp); err != nil { + return err + } + + if resp.IsError() { + return resp.ToError() + } + + inner, err := xml.Marshal(resp.OutConfigs) + if err != nil { + return err + } + + // The requested managed objects are contained within the inner XML document, + // which we need to unmarshal first into the given concrete type. + return xml.Unmarshal(inner, &out) +} + +// ConfigResolveChildren retrieves children of managed objects under a specified DN. +func (c *Client) ConfigResolveChildren(ctx context.Context, in ConfigResolveChildrenRequest, out mo.Any) error { + var resp ConfigResolveChildrenResponse + + if err := c.Request(ctx, in, &resp); err != nil { + return err + } + + if resp.IsError() { + return resp.ToError() + } + + inner, err := xml.Marshal(resp.OutConfigs) + if err != nil { + return err + } + + // The requested managed objects are contained within the inner XML document, + // which we need to unmarshal first into the given concrete type. + return xml.Unmarshal(inner, &out) +} diff --git a/local/go-ucs/api/doc.go b/local/go-ucs/api/doc.go new file mode 100755 index 0000000..7fbdcf5 --- /dev/null +++ b/local/go-ucs/api/doc.go @@ -0,0 +1,2 @@ +// Package api provides types and methods for interfacing with a Cisco UCS API endpoint. +package api diff --git a/local/go-ucs/api/example_aaa_keepalive_test.go b/local/go-ucs/api/example_aaa_keepalive_test.go new file mode 100755 index 0000000..1983ee7 --- /dev/null +++ b/local/go-ucs/api/example_aaa_keepalive_test.go @@ -0,0 +1,73 @@ +package api_test + +import ( + "context" + "crypto/tls" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/dnaeon/go-ucs/api" +) + +func Example_aaaKeepAlive() { + // The following example shows how to keep a session alive. + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + // Authenticate to the remote API endpoint and obtain authentication cookie + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to authenticate: %s", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + // Channel on which the shutdown signal is sent + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + + // Send a KeepAlive request every minute + ticker := time.NewTicker(1 * time.Minute) + +L: + for { + select { + case <-quit: + log.Println("Keyboard interrupt detected, terminating.") + break L + case <-ticker.C: + log.Println("Sending KeepAlive request ...") + resp, err := client.AaaKeepAlive(ctx) + if err != nil { + log.Printf("Unable to keep session alive: %s\n", err) + break L + } + + log.Printf("Got response with cookie: %s\n", resp.Cookie) + } + } +} diff --git a/local/go-ucs/api/example_aaa_login_test.go b/local/go-ucs/api/example_aaa_login_test.go new file mode 100755 index 0000000..ad87e4a --- /dev/null +++ b/local/go-ucs/api/example_aaa_login_test.go @@ -0,0 +1,44 @@ +package api_test + +import ( + "context" + "crypto/tls" + "log" + "net/http" + + "github.com/dnaeon/go-ucs/api" +) + +func Example_aaaLogin() { + // The following example shows how to login and logout from a Cisco UCS API endpoint + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s\n", err) + } + + // Authenticate to the remote API endpoint and obtain authentication cookie + log.Printf("Logging in to %s\n", config.Endpoint) + + ctx := context.Background() + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to authenticate: %s", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) +} diff --git a/local/go-ucs/api/example_aaa_refresh_test.go b/local/go-ucs/api/example_aaa_refresh_test.go new file mode 100755 index 0000000..4fe608a --- /dev/null +++ b/local/go-ucs/api/example_aaa_refresh_test.go @@ -0,0 +1,51 @@ +package api_test + +import ( + "context" + "crypto/tls" + "log" + "net/http" + + "github.com/dnaeon/go-ucs/api" +) + +func Example_aaaRefresh() { + // The following example shows how to refresh an existing session. + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + // Authenticate to the remote API endpoint and obtain authentication cookie + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to authenticate: %s", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + log.Println("Refreshing session") + if _, err := client.AaaRefresh(ctx); err != nil { + log.Fatalf("Unable to refresh session: %s\n", err) + } + + log.Printf("New authentication cookie is: %s\n", client.Cookie) +} diff --git a/local/go-ucs/api/example_composite_filter_test.go b/local/go-ucs/api/example_composite_filter_test.go new file mode 100755 index 0000000..3ac520c --- /dev/null +++ b/local/go-ucs/api/example_composite_filter_test.go @@ -0,0 +1,96 @@ +package api_test + +import ( + "context" + "crypto/tls" + "encoding/xml" + "log" + "net/http" + + "github.com/dnaeon/go-ucs/api" + "github.com/dnaeon/go-ucs/mo" +) + +func Example_compositeFilter() { + // The following example shows how to use a composite AND property filter. + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to login: %s\n", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + // The type into which we unmarshal the result data + type blades struct { + XMLName xml.Name + Blades []mo.ComputeBlade `xml:"computeBlade"` + } + + // Create a composite AND property filter, which will find all blades, + // which have total memory greater than or equal to 2048 MB and are in chassis 3. + filter := api.FilterAnd{ + Filters: []api.FilterAny{ + api.FilterGe{ + FilterProperty: api.FilterProperty{ + Class: "computeBlade", + Property: "totalMemory", + Value: "2048", + }, + }, + api.FilterEq{ + FilterProperty: api.FilterProperty{ + Class: "computeBlade", + Property: "chassisId", + Value: "3", + }, + }, + }, + } + + req := api.ConfigResolveClassRequest{ + Cookie: client.Cookie, + InHierarchical: "false", + ClassId: "computeBlade", + InFilter: filter, + } + + var out blades + log.Println("Retrieving managed objects with class `computeBlade`") + if err := client.ConfigResolveClass(ctx, req, &out); err != nil { + log.Fatalf("Unable to retrieve `computeBlade` managed object: %s", err) + } + + log.Printf("Retrieved %d compute blades\n", len(out.Blades)) + + for _, blade := range out.Blades { + log.Printf("%s:\n", blade.Dn) + log.Printf("\tNumber of CPUs: %d\n", blade.NumOfCpus) + log.Printf("\tTotal Memory: %d\n", blade.TotalMemory) + log.Printf("\tModel: %s\n", blade.Model) + log.Printf("\tChassis ID: %d\n", blade.ChassisId) + log.Printf("\tVendor: %s\n", blade.Vendor) + } +} diff --git a/local/go-ucs/api/example_config_resolve_class_test.go b/local/go-ucs/api/example_config_resolve_class_test.go new file mode 100755 index 0000000..f146a8b --- /dev/null +++ b/local/go-ucs/api/example_config_resolve_class_test.go @@ -0,0 +1,73 @@ +package api_test + +import ( + "context" + "crypto/tls" + "encoding/xml" + "log" + "net/http" + + "github.com/dnaeon/go-ucs/api" + "github.com/dnaeon/go-ucs/mo" +) + +func Example_configResolveClass() { + // The following example shows how to retrieve all compute blades. + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to login: %s\n", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + // The type into which we unmarshal the result data + type blades struct { + XMLName xml.Name + Blades []mo.ComputeBlade `xml:"computeBlade"` + } + + req := api.ConfigResolveClassRequest{ + Cookie: client.Cookie, + ClassId: "computeBlade", + InHierarchical: "false", + } + + var out blades + + log.Println("Retrieving managed objects with class `computeBlade`") + if err := client.ConfigResolveClass(ctx, req, &out); err != nil { + log.Fatalf("Unable to retrieve `computeBlade` managed object: %s", err) + } + + log.Printf("Retrieved %d compute blades\n", len(out.Blades)) + for _, blade := range out.Blades { + log.Printf("%s:\n", blade.Dn) + log.Printf("\tNumber of CPUs: %d\n", blade.NumOfCpus) + log.Printf("\tTotal Memory: %d\n", blade.TotalMemory) + log.Printf("\tModel: %s\n", blade.Model) + log.Printf("\tVendor: %s\n", blade.Vendor) + } +} diff --git a/local/go-ucs/api/example_config_resolve_classes_test.go b/local/go-ucs/api/example_config_resolve_classes_test.go new file mode 100755 index 0000000..817135d --- /dev/null +++ b/local/go-ucs/api/example_config_resolve_classes_test.go @@ -0,0 +1,81 @@ +package api_test + +import ( + "context" + "crypto/tls" + "log" + "net/http" + + "github.com/dnaeon/go-ucs/api" + "github.com/dnaeon/go-ucs/mo" +) + +func Example_configResolveClasses() { + // The following example shows how to retrieve managed objects from different classes. + // In the example below we will retrieve the managed objects of `computeBlade` and `computeRackUnit` classes. + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to login: %s\n", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + req := api.ConfigResolveClassesRequest{ + Cookie: client.Cookie, + InHierarchical: "false", + InIds: []api.Id{ + api.NewId("computeBlade"), + api.NewId("computeRackUnit"), + }, + } + + // ComputeItem is a container for all physical compute items. + var out mo.ComputeItem + + log.Println("Retrieving managed objects with classes `computeBlade` and `computeRackUnit`") + if err := client.ConfigResolveClasses(ctx, req, &out); err != nil { + log.Fatalf("Unable to retrieve `computeBlade` and `computeRackUnit` managed object: %s", err) + } + + log.Printf("Retrieved %d compute blades\n", len(out.Blades)) + log.Printf("Retrieved %d compute rack units\n", len(out.RackUnits)) + + for _, blade := range out.Blades { + log.Printf("%s:\n", blade.Dn) + log.Printf("\tNumber of CPUs: %d\n", blade.NumOfCpus) + log.Printf("\tTotal Memory: %d\n", blade.TotalMemory) + log.Printf("\tModel: %s\n", blade.Model) + log.Printf("\tVendor: %s\n", blade.Vendor) + } + + for _, blade := range out.RackUnits { + log.Printf("%s:\n", blade.Dn) + log.Printf("\tNumber of CPUs: %d\n", blade.NumOfCpus) + log.Printf("\tTotal Memory: %d\n", blade.TotalMemory) + log.Printf("\tModel: %s\n", blade.Model) + log.Printf("\tVendor: %s\n", blade.Vendor) + } +} diff --git a/local/go-ucs/api/example_config_resolve_dn_test.go b/local/go-ucs/api/example_config_resolve_dn_test.go new file mode 100755 index 0000000..b768044 --- /dev/null +++ b/local/go-ucs/api/example_config_resolve_dn_test.go @@ -0,0 +1,63 @@ +package api_test + +import ( + "context" + "crypto/tls" + "log" + "net/http" + + "github.com/dnaeon/go-ucs/api" + "github.com/dnaeon/go-ucs/mo" +) + +func Example_configResolveDn() { + // The following example shows how to retrieve a single managed object for a specified DN. + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to login: %s\n", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + // Retrieve the `sys` DN, which is of type mo.TopSystem + log.Println("Retrieving `sys` managed object") + req := api.ConfigResolveDnRequest{ + Cookie: client.Cookie, + Dn: "sys", + InHierarchical: "false", + } + + var sys mo.TopSystem + if err := client.ConfigResolveDn(ctx, req, &sys); err != nil { + log.Fatalf("Unable to retrieve DN: %s", err) + } + + log.Printf("Address: %s\n", sys.Address) + log.Printf("Current time: %s\n", sys.CurrentTime) + log.Printf("Dn: %s\n", sys.Dn) + log.Printf("Mode: %s\n", sys.Mode) + log.Printf("Uptime: %s\n", sys.SystemUptime) +} diff --git a/local/go-ucs/api/example_config_resolve_dns_test.go b/local/go-ucs/api/example_config_resolve_dns_test.go new file mode 100755 index 0000000..cd0aa85 --- /dev/null +++ b/local/go-ucs/api/example_config_resolve_dns_test.go @@ -0,0 +1,80 @@ +package api_test + +import ( + "context" + "crypto/tls" + "encoding/xml" + "log" + "net/http" + "strings" + + "github.com/dnaeon/go-ucs/api" + "github.com/dnaeon/go-ucs/mo" +) + +func Example_configResolveDns() { + // The following example shows how to retrieve a list of managed objects by specifying their DNs. + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to login: %s\n", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + // A type to contain the instances of the retrieved DNs. + type outConfigs struct { + XMLName xml.Name + Sys mo.TopSystem `xml:"topSystem"` + Version mo.VersionApplication `xml:"versionApplication"` + } + + var out outConfigs + + // Retrieve the list of DNs + log.Println("Retrieving managed objects using configResolveDns query method") + req := api.ConfigResolveDnsRequest{ + Cookie: client.Cookie, + InHierarchical: "false", + InDns: []api.Dn{ + api.NewDn("sys"), + api.NewDn("sys/version/application"), + api.NewDn("no/such/dn"), + }, + } + + resp, err := client.ConfigResolveDns(ctx, req, &out) + if err != nil { + log.Fatalf("Unable to retrieve DNs: %s", err) + } + + log.Printf("%s is at version %s\n", out.Sys.Name, out.Version.Version) + + unresolved := make([]string, 0) + for _, dn := range resp.OutUnresolved { + unresolved = append(unresolved, dn.Value) + } + log.Printf("Unresolved DNs: %s", strings.Join(unresolved, ", ")) +} diff --git a/local/go-ucs/api/example_rate_limiter_test.go b/local/go-ucs/api/example_rate_limiter_test.go new file mode 100755 index 0000000..9170bba --- /dev/null +++ b/local/go-ucs/api/example_rate_limiter_test.go @@ -0,0 +1,88 @@ +package api_test + +import ( + "context" + "crypto/tls" + "log" + "net/http" + "sync" + "time" + + "github.com/dnaeon/go-ucs/api" + "github.com/dnaeon/go-ucs/mo" +) + +func Example_rateLimiter() { + // The following example shows how to rate limit requests again the remote + // Cisco UCS API endpoint using a token bucket rate limiter. + // https://en.wikipedia.org/wiki/Token_bucket + + // Skip SSL certificate verification of remote endpoint. + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + httpClient := &http.Client{Transport: tr} + + // Create a new Cisco UCS API client. + // Set maximum allowed requests per second to 1 with a burst size of 1. + // A request will wait up to 1 minute for a token. + config := api.Config{ + Endpoint: "https://ucs01.example.org/", + Username: "admin", + Password: "password", + HttpClient: httpClient, + RateLimit: &api.RateLimit{ + RequestsPerSecond: 1.0, + Burst: 1, + Wait: time.Duration(1 * time.Minute), + }, + } + + client, err := api.NewClient(config) + if err != nil { + log.Fatalf("Unable to create API client: %s", err) + } + + ctx := context.Background() + + log.Printf("Logging in to %s\n", config.Endpoint) + if _, err := client.AaaLogin(ctx); err != nil { + log.Fatalf("Unable to login: %s\n", err) + } + defer client.AaaLogout(ctx) + + log.Printf("Got authentication cookie: %s\n", client.Cookie) + + // Start a few concurrent requests to the remote API endpoint. + // Requests will be executed one at a time, because of how the limiter is configured. + var wg sync.WaitGroup + for i := 1; i < 10; i++ { + wg.Add(1) + + // Our worker function will retrieve the `sys` DN from the remote Cisco UCS API. + // We will start a few of these in separate goroutines. + worker := func(id int) { + defer wg.Done() + + // Retrieve the `sys` DN, which is of type mo.TopSystem + log.Printf("Worker #%d: Retrieving `sys` managed object\n", id) + req := api.ConfigResolveDnRequest{ + Cookie: client.Cookie, + Dn: "sys", + InHierarchical: "false", + } + + var sys mo.TopSystem + if err := client.ConfigResolveDn(ctx, req, &sys); err != nil { + log.Printf("Worker #%d: Unable to retrieve DN: %s\n", id, err) + return + } + + log.Printf("Worker #%d: successfully retrieved `sys` managed object\n", id) + } + + go worker(i) + } + + wg.Wait() +} diff --git a/local/go-ucs/api/xml.go b/local/go-ucs/api/xml.go new file mode 100755 index 0000000..08ce52c --- /dev/null +++ b/local/go-ucs/api/xml.go @@ -0,0 +1,54 @@ +package api + +import ( + "encoding/xml" + "regexp" +) + +// xmlMarshalWithSelfClosingTags post-processes results from xml.Marshal into XML +// document where empty XML elements use self-closing tags. +// +// The XML standard states that self-closing tags are permitted. +// +// https://www.w3.org/TR/REC-xml/#dt-empty +// https://www.w3.org/TR/REC-xml/#d0e2480 +// +// According to the XML standard an empty element with a start and end tag is +// semantically the same as an empty element with a self-closing tag. +// +// Unfortunately not all XML parsers can understand both. Such is the +// case with Cisco UCS Manager API, which expects empty elements to use +// self-closing tags only. +// +// As of now XML marshaling in Go always uses start and end tags, +// which results in XML elements like the one below. +// +// +// +// Above XML elements cannot be parsed by the remote Cisco UCS API endpoint, +// and such API calls result in parse error returned to the client. +// +// Currently the Go team is considering implementing XML self-closing tags, +// which progress can be tracked in the issue below. +// +// https://github.com/golang/go/issues/21399 +// +// Until support for XML self-closing tags (if ever) becomes real in Go +// we need to ensure compatibility with the Cisco UCS API by doing that ourselves. +// +// In a separate thread @rsc also suggested a similar approach by using +// strings.Replace(), even though such thing is not ideal and should +// hopefully one day make into the language as a feature. +// +// https://groups.google.com/forum/#!topic/golang-nuts/guG6iOCRu08 +func xmlMarshalWithSelfClosingTags(in interface{}) ([]byte, error) { + data, err := xml.Marshal(in) + if err != nil { + return nil, err + } + + re := regexp.MustCompile(`<([^/][\w\s\"\=\-\/]*)>\s*<(\/\w*)>`) + newData := re.ReplaceAllString(string(data), "<$1/>") + + return []byte(newData), nil +} diff --git a/local/go-ucs/api/xml_test.go b/local/go-ucs/api/xml_test.go new file mode 100755 index 0000000..798d235 --- /dev/null +++ b/local/go-ucs/api/xml_test.go @@ -0,0 +1,64 @@ +package api + +import ( + "encoding/xml" + "testing" +) + +type Person struct { + XMLName xml.Name `xml:"person"` + Name string `xml:"name,attr,omitempty"` + Place *Location `xml:"place,omitempty"` +} + +type Location struct { + Country string `xml:"country,omitempty"` + City string `xml:"city,omitempty"` +} + +type PersonEmbedded struct { + XMLName xml.Name `xml:"personEmbedded"` + Person + Location +} + +type NilStruct *Person + +func TestXmlMarshalWithSelfClosingTags(t *testing.T) { + var tests = []struct { + value interface{} + expect string + }{ + // Nil values + {value: nil, expect: ``}, + {value: new(NilStruct), expect: ``}, + + // Values + {value: Person{}, expect: ``}, + {value: Person{Name: "John Doe"}, expect: ``}, + { + value: Person{Name: "Jane Doe", Place: &Location{Country: "unknown", City: "unknown"}}, + expect: `unknownunknown`, + }, + { + value: &PersonEmbedded{Person: Person{Name: "John Doe"}, Location: Location{Country: "unknown"}}, + expect: `unknown`, + }, + + // Pointers to values + {value: &Person{}, expect: ``}, + {value: &Person{Name: "John Doe"}, expect: ``}, + } + + for _, test := range tests { + data, err := xmlMarshalWithSelfClosingTags(test.value) + if err != nil { + t.Fatalf("Cannot marshal %+v: %s", test.value, err) + } + + got := string(data) + if got != test.expect { + t.Fatalf("Got XML '%s', expect '%s'", got, test.expect) + } + } +} diff --git a/local/go-ucs/go.mod b/local/go-ucs/go.mod new file mode 100755 index 0000000..42c24c1 --- /dev/null +++ b/local/go-ucs/go.mod @@ -0,0 +1,11 @@ +module local/go-ucs + +go 1.21.0 + +require ( + golang.org/x/net v0.0.0-20180420171651-5f9ae10d9af5 + golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 +) +replace github.com/dnaeon/go-ucs/mo => ./local/go-ucs/mo +replace github.com/dnaeon/go-ucs/version => ./local/go-ucs/version +replace github.com/dnaeon/go-ucs/api => ./local/go-ucs/api \ No newline at end of file diff --git a/local/go-ucs/mo/doc.go b/local/go-ucs/mo/doc.go new file mode 100755 index 0000000..9e8e9d9 --- /dev/null +++ b/local/go-ucs/mo/doc.go @@ -0,0 +1,2 @@ +// Package mo provides Go types for the Cisco UCS Managed Objects types. +package mo diff --git a/local/go-ucs/mo/mo.go b/local/go-ucs/mo/mo.go new file mode 100755 index 0000000..b5b945d --- /dev/null +++ b/local/go-ucs/mo/mo.go @@ -0,0 +1,750 @@ +package mo + +import ( + "encoding/xml" + "net" +) + +// Any represents any valid managed object. +type Any interface{} + +// TopSystem provides general information about the system, such as the +// name, IP address and current time. +type TopSystem struct { + XMLName xml.Name `xml:"topSystem"` + Address net.IP `xml:"address,attr,omitempty"` + CurrentTime string `xml:"currentTime,attr,omitempty"` + Description string `xml:"descr,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + Ipv6Addr string `xml:"ipv6Addr,attr,omitempty"` + Mode string `xml:"mode,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + Owner string `xml:"owner,attr,omitempty"` + Site string `xml:"site,attr,omitempty"` + SystemUptime string `xml:"systemUpTime,attr,omitempty"` + VersionEp VersionEp `xml:"versionEp"` + CommServiceEp CommServiceEp `xml:"commSvcEp"` + EquipmentChassis []EquipmentChassis `xml:"equipmentChassis"` + ComputeRackUnits []ComputeRackUnit `xml:"computeRackUnit"` +} + +// CommServiceEp contains configuration for various services. +type CommServiceEp struct { + FiniteStateMachineTask + XMLName xml.Name `xml:"commSvcEp"` + ConfigState string `xml:"configState,attr,omitempty"` + ConfigStatusMessage string `xml:"configStatusMessage,attr,omitempty"` + Description string `xml:"descr,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + IntId string `xml:"intId,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + PolicyLevel int `xml:"policyLevel,attr,omitempty"` + PolicyOwner string `xml:"policyOwner,attr,omitempty"` + CommDns CommDns `xml:"commDns"` +} + +// CommDns contains the DNS settings of the UCS system. +type CommDns struct { + XMLName xml.Name `xml:"commDns"` + AdminState string `xml:"adminState,attr,omitempty"` + Description string `xml:"descr,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + Domain string `xml:"domain,attr,omitempty"` + IntId string `xml:"intId,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + OperationalPort int `xml:"operPort,attr,omitempty"` + PolicyLevel int `xml:"policyLevel,attr,omitempty"` + PolicyOwner string `xml:"policyOwner,attr,omitempty"` + Port int `xml:"port,attr,omitempty"` + Proto string `xml:"proto,attr,omitempty"` + Providers []CommDnsProvider `xml:"commDnsProvider"` +} + +// CommDnsProvider represents a DNS service provider. +type CommDnsProvider struct { + XMLName xml.Name `xml:"commDnsProvider"` + AdminState string `xml:"adminState,attr,omitempty"` + Description string `xml:"descr,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + Hostname string `xml:"hostname,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` +} + +// VersionEp contains version information. +type VersionEp struct { + XMLName xml.Name `xml:"versionEp"` + ChildAction string `xml:"childAction,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + Application VersionApplication `xml:"versionApplication,omitempty"` +} + +// VersionApplication contains the application version. +type VersionApplication struct { + XMLName xml.Name `xml:"versionApplication"` + ChildAction string `xml:"childAction,attr,omitempty"` + Detail string `xml:"detail,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Time string `xml:"time,attr,omitempty"` + Version string `xml:"version,attr,omitempty"` +} + +// EquipmentChassis represents a physical unit that can accomodate multiple blade servers. +// For example, the Cisco UCS 5108 Blade Server Chassis is six rack units (6RU) high, +// can mount in an industry-standard 19-inch rack and uses front-to-back cooling. +type EquipmentChassis struct { + FiniteStateMachineTask + XMLName xml.Name `xml:"equipmentChassis"` + AckProgressIndicator string `xml:"ackProgressIndicator,attr,omitempty"` + AdminState string `xml:"adminState,attr,omitempty"` + AssignedToDn string `xml:"assignedToDn,attr,omitempty"` + Association string `xml:"association,attr,omitempty"` + Availability string `xml:"availability,attr,omitempty"` + ConfigState string `xml:"configState,attr,omitempty"` + ConnPath string `xml:"connPath,attr,omitempty"` + ConnStatus string `xml:"connStatus,attr,omitempty"` + Discovery string `xml:"discovery,attr,omitempty"` + DiscoveryStatus string `xml:"discoveryStatus,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + FabricEpDn string `xml:"fabricEpDn,attr,omitempty"` + FltAggr int `xml:"fltAggr,attr,omitempty"` + Id string `xml:"id,attr,omitempty"` + LcTimestamp string `xml:"lcTs,attr,omitempty"` + LicGP int `xml:"licGP,attr,omitempty"` + LicState string `xml:"licState,attr,omitempty"` + ManagingInstance string `xml:"managingInst,attr,omitempty"` + ManufacturingTime string `xml:"mfgTime,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalQualifier string `xml:"operQualifier,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + PartNumber string `xml:"partNumber,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + SeepromOperationalState string `xml:"seepromOperState,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + ServiceState string `xml:"serviceState,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + ThermalStateQualifier string `xml:"thermalStateQualifier,attr,omitempty"` + UserLabel string `xml:"usrLbl,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + VersionHolder string `xml:"versionHolder,attr,omitempty"` + Vid string `xml:"vid,attr,omitempty"` + ComputeBlades []ComputeBlade `xml:"computeBlade"` + FanModules []EquipmentFanModule `xml:"equipmentFanModule"` +} + +// ComputePhysical represents a physical specification of an abstract compute item. +// Serves as the base of physical compute nodes (e.g. blade, stand-alone computer or server). +type ComputePhysical struct { + FiniteStateMachineTask + AdminPower string `xml:"adminPower,attr,omitempty"` + AdminState string `xml:"adminState,attr,omitempty"` + AssignedToDn string `xml:"assignedToDn,attr,omitempty"` + Association string `xml:"association,attr,omitempty"` + Availability string `xml:"availability,attr,omitempty"` + AvailableMemory int `xml:"availableMemory,attr,omitempty"` + ChassisId string `xml:"chassisId,attr,omitempty"` + CheckPoint string `xml:"checkPoint,attr,omitempty"` + ConnPath string `xml:"connPath,attr,omitempty"` + ConnStatus string `xml:"connStatus,attr,omitempty"` + Description string `xml:"descr,attr,omitempty"` + Discovery string `xml:"discovery,attr,omitempty"` + DiscoveryStatus string `xml:"discoveryStatus,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + FltAggr int `xml:"fltAggr,attr,omitempty"` + IntId string `xml:"intId,attr,omitempty"` + Lc string `xml:"lc,attr,omitempty"` + LcTimestamp string `xml:"lcTs,attr,omitempty"` + LocalId string `xml:"localId,attr,omitempty"` + LowVoltageMemory string `xml:"lowVoltageMemory,attr,omitempty"` + ManagingInstance string `xml:"managingInst,attr,omitempty"` + MemorySpeed string `xml:"memorySpeed,attr,omitempty"` + ManufacturingTime string `xml:"mfgTime,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + NumOf40GAdaptorsWithOldFirmware int `xml:"numOf40GAdaptorsWithOldFw,attr,omitempty"` + NumOf40GAdaptorsWithUnknownFirmware int `xml:"numOf40GAdaptorsWithUnknownFw,attr,omitempty"` + NumOfAdaptors int `xml:"numOfAdaptors,attr,omitempty"` + NumOfCores int `xml:"numOfCores,attr,omitempty"` + NumOfCoresEnabled int `xml:"numOfCoresEnabled,attr,omitempty"` + NumOfCpus int `xml:"numOfCpus,attr,omitempty"` + NumOfEthHostInterfaces int `xml:"numOfEthHostIfs,attr,omitempty"` + NumOfFcHostInterfaces int `xml:"numOfFcHostIfs,attr,omitempty"` + NumOfThreads int `xml:"numOfThreads,attr,omitempty"` + OperationalPower string `xml:"operPower,attr,omitempty"` + OperationalPowerTransitionSource string `xml:"operPwrTransSrc,attr,omitempty"` + OperationalQualifier string `xml:"operQualifier,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + OriginalUuid string `xml:"originalUuid,attr,omitempty"` + PartNumber string `xml:"partNumber,attr,omitempty"` + PolicyLevel int `xml:"policyLevel,attr,omitempty"` + PolicyOwner string `xml:"policyOwner,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + ScaledMode string `xml:"scaledMode,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + ServerId string `xml:"serverId,attr,omitempty"` + SlotId int `xml:"slotId,attr,omitempty"` + TotalMemory int `xml:"totalMemory,attr,omitempty"` + UserLabel string `xml:"usrLbl,attr,omitempty"` + Uuid string `xml:"uuid,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Vid string `xml:"vid,attr,omitempty"` + ComputeBoard ComputeBoard `xml:"computeBoard"` + AdaptorUnits []AdaptorUnit `xml:"adaptorUnit"` + ManagementController ManagementController `xml:"mgmtController"` + FirmwareStatus FirmwareStatus `xml:"firmwareStatus"` + BiosUnit BiosUnit `xml:"biosUnit"` +} + +// ComputeBlade represents a physical compute blade. +// Physical compute item in blade form factor. +type ComputeBlade struct { + XMLName xml.Name `xml:"computeBlade"` + ComputePhysical +} + +// ComputeRackUnit represents a physical compute RackUnit. +// Physical compute item representing a Rack mountable unit. +type ComputeRackUnit struct { + XMLName xml.Name `xml:"computeRackUnit"` + ComputePhysical +} + +// ComputeServerUnit represents a server instance on a cartridge. +type ComputeServerUnit struct { + XMLName xml.Name `xml:"computeServerUnit"` + ComputePhysical +} + +// ComputeItem type represents a container for all compute items, +// which include blades, rack units and stand-alone servers. +type ComputeItem struct { + XMLName xml.Name + Blades []ComputeBlade `xml:"computeBlade"` + RackUnits []ComputeRackUnit `xml:"computeRackUnit"` + ServerUnits []ComputeServerUnit `xml:"computeServerUnit"` +} + +// ComputeBoard represents a motherboard contained by physical compute item. +type ComputeBoard struct { + XMLName xml.Name `xml:"computeBoard"` + CmosVoltage string `xml:"cmosVoltage,attr,omitempty"` + CpuTypeDescription string `xml:"cpuTypeDescription,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + FaultQualifier string `xml:"faultQualifier,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + LocationDn string `xml:"locationDn,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalPower string `xml:"operPower,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + PowerUsage string `xml:"powerUsage,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` + MemoryArray MemoryArray `xml:"memoryArray"` + ProcessorUnits []ProcessorUnit `xml:"processorUnit"` + StorageController StorageController `xml:"storageController"` +} + +// ComputeMbTempStats represents temperature values added by Nathan +// Properties are incomplete +type ComputeMbTempStats struct { + XMLName xml.Name `xml:"computeMbTempStats"` + Dn string `xml:"dn,attr,omitempty"` + FmTempSenIo string `xml:"fmTempSenIo,attr,omitempty"` + FmTempSenRear string `xml:"fmTempSenRear,attr,omitempty"` +} + +// MemoryArray represents an array of memory units. +type MemoryArray struct { + XMLName xml.Name `xml:"memoryArray"` + ChildAction string `xml:"childAction,attr,omitempty"` + CpuId int `xml:"cpuId,attr,omitempty"` + CurrentCapacity int `xml:"currCapacity,attr,omitempty"` + ErrorCorrectionn string `xml:"errorCorrection,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + LocationDn string `xml:"locationDn,attr,omitempty"` + MaxCapacity int `xml:"maxCapacity,attr,omitempty"` + MaxDevices int `xml:"maxDevices,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + Populated int `xml:"populated,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` + Units []MemoryUnit `xml:"memoryUnit"` +} + +// MemoryUnit represents a single memory unit in a memory array. +type MemoryUnit struct { + XMLName xml.Name `xml:"memoryUnit"` + AdminState string `xml:"adminState,attr,omitempty"` + Array int `xml:"array,attr,omitempty"` + Bank int `xml:"bank,attr,omitempty"` + Capacity string `xml:"capacity,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + Clock string `xml:"clock,attr,omitempty"` + FormFactor string `xml:"formFactor,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + Latency string `xml:"latency,attr,omitempty"` + Location string `xml:"location,attr,omitempty"` + LocationDn string `xml:"locationDn,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalQualifier string `xml:"operQualifier,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Set int `xml:"set,attr,omitempty"` + Speed string `xml:"speed,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Visibility string `xml:"visibility,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` + Width string `xml:"width,attr,omitempty"` +} + +// ProcessorUnit represents a single processor unit. +type ProcessorUnit struct { + XMLName xml.Name `xml:"processorUnit"` + Arch string `xml:"arch,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + Cores int `xml:"cores,attr,omitempty"` + CoresEnabled int `xml:"coresEnabled,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + LocationDn string `xml:"locationDn,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + SocketDesignation string `xml:"socketDesignation,attr,omitempty"` + Speed string `xml:"speed,attr,omitempty"` + Stepping int `xml:"stepping,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Threads int `xml:"threads,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Visibility string `xml:"visibility,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` +} + +// AdaptorUnit is a managed object representing a network adaptor unit such as a +// card that has NIC and/or HBA, SCSI functionality. +type AdaptorUnit struct { + XMLName xml.Name `xml:"adaptorUnit"` + AdminPowerState string `xml:"adminPowerState,attr,omitempty"` + BaseMac string `xml:"baseMac,attr,omitempty"` + BladeId int `xml:"bladeId,attr,omitempty"` + CartridgeId int `xml:"cartridgeId,attr,omitempty"` + ChassisId string `xml:"chassisId,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + ConnPath string `xml:"connPath,attr,omitempty"` + ConnStatus string `xml:"connStatus,attr,omitempty"` + DiscoveryStatus string `xml:"discoveryStatus,attr,omitempty"` + FltAggr int `xml:"fltAggr,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + Integrated string `xml:"integrated,attr,omitempty"` + LocationDn string `xml:"locationDn,attr,omitempty"` + ManagingInstance string `xml:"managingInst,attr,omitempty"` + ManufacturingTime string `xml:"mfgTime,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + PartNumber string `xml:"partNumber,attr,omitempty"` + PciAddress string `xml:"pciAddr,attr,omitempty"` + PciSlot string `xml:"pciSlot,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Reachability string `xml:"reachability,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Vid string `xml:"vid,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` + AdaptorHostEthernetInterfaces []AdaptorHostEthernetInterface `xml:"adaptorHostEthIf"` + ManagementController ManagementController `xml:"mgmtController"` +} + +// AdaptorHostEthernetInterface is a representation of a host-facing Ethernet interface +// on a server adaptor. A server adaptor has network facing interfaces (NIF), which +// provide network connectivity to the network (through the IO Module for UCS blades) and +// server facing interfaces (SIF), which are visible by the Operating System. +type AdaptorHostEthernetInterface struct { + FiniteStateMachineTask + XMLName xml.Name `xml:"adaptorHostEthIf"` + AdminState string `xml:"adminState,attr,omitempty"` + BootDev string `xml:"bootDev,attr,omitempty"` + CdnName string `xml:"cdnName,attr,omitempty"` + ChassisId string `xml:"chassisId,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + Discovery string `xml:"discovery,attr,omitempty"` + EpDn string `xml:"epDn,attr,omitempty"` + FltAggr int `xml:"fltAggr,attr,omitempty"` + HostPort string `xml:"hostPort,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + InterfaceRole string `xml:"ifRole,attr,omitempty"` + InterfaceType string `xml:"ifType,attr,omitempty"` + Lc string `xml:"lc,attr,omitempty"` + LinkState string `xml:"linkState,attr,omitempty"` + Locale string `xml:"locale,attr,omitempty"` + Mac string `xml:"mac,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + Mtu int `xml:"mtu,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + Order int `xml:"order,attr,omitempty"` + OriginalMac string `xml:"originaMac,attr,omitempty"` + PciAddress string `xml:"pciAddr,attr,omitempty"` + PciFunc int `xml:"pciFunc,attr,omitempty"` + PciSlot int `xml:"pciSlot,attr,omitempty"` + PeerChassisId string `xml:"peerChassisId,attr,omitempty"` + PeerDn string `xml:"peerDn,attr,omitempty"` + PeerPortId int `xml:"peerPortId,attr,omitempty"` + PeerSlotId int `xml:"peerSlotId,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + PfDn string `xml:"pfDn,attr,omitempty"` + PortId int `xml:"portId,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Purpose string `xml:"purpose,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Side string `xml:"side,attr,omitempty"` + SlotId int `xml:"slotId,attr,omitempty"` + SwitchId string `xml:"switchId,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Transport string `xml:"transport,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + VirtualizationPreference string `xml:"virtualizationPreference,attr,omitempty"` + VnicDn string `xml:"vnicDn,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` + ManagementInterfaces []ManagementInterface `xml:"mgmtIf"` +} + +// ManagementInterface encapsulates the configuration of a CIMC management interface. +type ManagementInterface struct { + FiniteStateMachineTask + XMLName xml.Name `xml:"mgmtIf"` + Access string `xml:"access,attr,omitempty"` + AdminState string `xml:"adminState,attr,omitempty"` + AggrPortId int `xml:"aggrPortId,attr,omitempty"` + ChassisId string `xml:"chassisId,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + Discovery string `xml:"discovery,attr,omitempty"` + EpDn string `xml:"epDn,attr,omitempty"` + ExtBroadcast net.IP `xml:"extBroadcast,attr,omitempty"` + ExtGateway net.IP `xml:"extGw,attr,omitempty"` + ExtIp net.IP `xml:"extIp,attr,omitempty"` + ExtNetmask net.IP `xml:"extMask,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + InterfaceRole string `xml:"ifRole,attr,omitempty"` + InterfaceType string `xml:"ifType,attr,omitempty"` + InstanceId int `xml:"instanceId,attr,omitempty"` + Locale string `xml:"locale,attr,omitempty"` + Ip net.IP `xml:"ip,attr,omitempty"` + Mac string `xml:"mac,attr,omitempty"` + Netmask net.IP `xml:"mask,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + PeerAggrPortId int `xml:"peerAggrPortId,attr,omitempty"` + PeerChassisId string `xml:"peerChassisId,attr,omitempty"` + PeerDn string `xml:"peerDn,attr,omitempty"` + PeerPortId int `xml:"peerPortId,attr,omitempty"` + PeerSlotId int `xml:"peerSlotId,attr,omitempty"` + PortId int `xml:"portId,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + SlotId int `xml:"slotId,attr,omitempty"` + StateQual string `xml:"stateQual,attr,omitempty"` + Subject string `xml:"subject,attr,omitempty"` + SwitchId string `xml:"switchId,attr,omitempty"` + Transport string `xml:"transport,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Vnet int `xml:"vnet,attr,omitempty"` +} + +// EquipmentFanModule represents an inventoried Fan module. +// This object is created implicitly when a Fan module is detected during equipment discovery. +type EquipmentFanModule struct { + XMLName xml.Name `xml:"equipmentFanModule"` + ChildAction string `xml:"childAction,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + FltAggr int `xml:"fltAggr,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + ManufacturingTime string `xml:"mfgTime,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalQualifier string `xml:"operQualifier,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + PartNumber string `xml:"partNumber,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Tray int `xml:"tray,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Vid string `xml:"vid,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` + Fans []EquipmentFan `xml:"equipmentFan"` +} + +// EquipmentFan represents a fan in a Fan module. +type EquipmentFan struct { + XMLName xml.Name `xml:"equipmentFan"` + ChildAction string `xml:"childAction,attr,omitempty"` + FanSpeedPolicyAdminState string `xml:"fanSpeedPolicyAdminState,attr,omitempty"` + FanSpeedPolicyOperationalState string `xml:"fanSpeedPolicyOperState,attr,omitempty"` + FltAggr int `xml:"fltAggr,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + InternalType string `xml:"intType,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + Module int `xml:"module,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Tray int `xml:"tray,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` +} + +// FiniteStateMachineTask represents the result of an FSM task. +type FiniteStateMachineTask struct { + FsmDescription string `xml:"fsmDescr,attr,omitempty"` + FsmFlags string `xml:"fsmFlags,attr,omitempty"` + FsmPrev string `xml:"fsmPrev,attr,omitempty"` + FsmProgress int `xml:"fsmProgr,attr,omitempty"` + FsmRemoteInvErrCode string `xml:"fsmRmtInvErrCode,attr,omitempty"` + FsmRemoteInvErrDescription string `xml:"fsmRmtInvErrDescr,attr,omitempty"` + FsmRemoteInvResult string `xml:"fsmRmtInvRslt,attr,omitempty"` + FsmStageDescription string `xml:"fsmStageDescr,attr,omitempty"` + FsmTimestamp string `xml:"fsmStamp,attr,omitempty"` + FsmStatus string `xml:"fsmStatus,attr,omitempty"` + FsmTry int `xml:"fsmTry,attr,omitempty"` +} + +// FirmwareRunning is a representation of the primary firmware image (currently running). +type FirmwareRunning struct { + XMLName xml.Name `xml:"firmwareRunning"` + Deployment string `xml:"deployment,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + InvTag string `xml:"invTag,attr,omitempty"` + PackageVersion string `xml:"packageVersion,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Version string `xml:"version,attr,omitempty"` +} + +// FirmwareUpdatable is a representation of a backup firmware image for the chassis components +// that supports backup image (CMC, BMC, BIOS, Adaptor, etc). +type FirmwareUpdatable struct { + XMLName xml.Name `xml:"firmwareUpdatable"` + AdminState string `xml:"adminState,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + Deployment string `xml:"deployment,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + OperationalStateQualifier string `xml:"operStateQual,attr,omitempty"` + PreviousVersion string `xml:"prevVersion,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Version string `xml:"version,attr,omitempty"` +} + +// FirmwareStatus represents a registered client for monitoring firmware update progress. +type FirmwareStatus struct { + XMLName xml.Name `xml:"firmwareStatus"` + ChildAction string `xml:"childAction,attr,omitempty"` + CimcVersion string `xml:"cimcVersion,attr,omitempty"` + FirmwareState string `xml:"firmwareState,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + PackageVersion string `xml:"packageVersion,attr,omitempty"` + PldVersion string `xml:"pldVersion,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` +} + +// StorageController represents a storage controller. +type StorageController struct { + XMLName xml.Name `xml:"storageController"` + AdminAction string `xml:"adminAction,attr,omitempty"` + AdminActionTrigger string `xml:"adminActionTrigger,attr,omitempty"` + ConfigState string `xml:"configState,attr,omitempty"` + ControllerOperations string `xml:"controllerOps,attr,omitempty"` + ControllerStatus string `xml:"controllerStatus,attr,omitempty"` + DefaultStripSize string `xml:"defaultStripSize,attr,omitempty"` + DeviceRaidSupport string `xml:"deviceRaidSupport,attr,omitempty"` + DiskOperations string `xml:"diskOps,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + FaultMonitoring string `xml:"faultMonitoring,attr,omitempty"` + HardwareRevision string `xml:"hwRevision,attr,omitempty"` + Id int `xml:"id,attr,omitempty"` + IdCount string `xml:"idCount,attr,omitempty"` + Lc string `xml:"lc,attr,omitempty"` + LocationDn string `xml:"locationDn,attr,omitempty"` + Mode string `xml:"mode,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OnBoardMemoryPresent string `xml:"onBoardMemoryPresent,attr,omitempty"` + OnBoardMemorySize string `xml:"onBoardMemorySize,attr,omitempty"` + OobControllerId string `xml:"oobControllerId,attr,omitempty"` + OobInterfaceSupported string `xml:"oobInterfaceSupported,attr,omitempty"` + OperationalQualifierReason string `xml:"operQualifierReason,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + OpromBootStatus string `xml:"opromBootStatus,attr,omitempty"` + PartNumber string `xml:"partNumber,attr,omitempty"` + PciAddress string `xml:"pciAddr,attr,omitempty"` + PciSlot string `xml:"pciSlot,attr,omitempty"` + PciSlotRawName string `xml:"pciSlotRawName,attr,omitempty"` + Perf string `xml:"perf,attr,omitempty"` + PinnedCacheStatus string `xml:"pinnedCacheStatus,attr,omitempty"` + Power string `xml:"power,attr,omitempty"` + Presence string `xml:"presence,attr,omitempty"` + RaidBatteryOperations string `xml:"raidBatteryOps,attr,omitempty"` + RaidSupport string `xml:"raidSupport,attr,omitempty"` + RebuildRate string `xml:"rebuildRate,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + SubOemId string `xml:"subOemId,attr,omitempty"` + SupportedStripSizes string `xml:"supportedStripSizes,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + VariantType string `xml:"variantType,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + Vid string `xml:"vid,attr,omitempty"` + VirtualDriveOperations string `xml:"virtualDriveops,attr,omitempty"` + Voltage string `xml:"voltage,attr,omitempty"` + ManagementController ManagementController `xml:"mgmtController"` + FirmwareRunning []FirmwareRunning `xml:"firmwareRunning"` +} + +// BiosUnit represents a BIOS unit. +type BiosUnit struct { + XMLName xml.Name `xml:"biosUnit"` + ChildAction string `xml:"childAction,attr,omitempty"` + InitSequence string `xml:"initSeq,attr,omitempty"` + InitTimestamp string `xml:"initTs,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + FirmwareRunning FirmwareRunning `xml:"firmwareRunning"` + FirmwareUpdatable FirmwareUpdatable `xml:"firmwareUpdatable"` +} + +// ManagementController represents an instance of a management controller. +type ManagementController struct { + FiniteStateMachineTask + XMLName xml.Name `xml:"mgmtController"` + DesiredMaintenanceMode string `xml:"desiredMaintenanceMode,attr,omitempty"` + DimmBlackListingOperationalState string `xml:"dimmBlacklistingOperState,attr,omitempty"` + DiskZoningState string `xml:"diskZoningState,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + Guid string `xml:"guid,attr,omitempty"` + Id string `xml:"id,attr,omitempty"` + LastRebootReason string `xml:"lastRebootReason,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OperationalConnection string `xml:"operConn,attr,omitempty"` + PowerFanSpeedPolicySupported string `xml:"powerFanSpeedPolicySupported,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + StorageOobConfigSupported string `xml:"storageOobConfigSupported,attr,omitempty"` + StorageOobInterfaceSupported string `xml:"storageOobInterfaceSupported,attr,omitempty"` + StorageSubsystemState string `xml:"storageSubsystemState,attr,omitempty"` + Subject string `xml:"subject,attr,omitempty"` + SupportedCapability string `xml:"supportedCapability,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + FirmwareRunning []FirmwareRunning `xml:"firmwareRunning"` + FirmwareUpdatable FirmwareUpdatable `xml:"firmwareUpdatable"` + ManagementInterfaces []ManagementInterface `xml:"mgmtIf"` +} + +// StorageItem represents a storage item. +type StorageItem struct { + XMLName xml.Name `xml:"storageItem"` + AlarmType string `xml:"alarmType,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + Name string `xml:"name,attr,omitempty"` + OperationalState string `xml:"operState,attr,omitempty"` + Rn string `xml:"rn,attr,omitempty"` + Size int `xml:"size,attr,omitempty"` + Used int `xml:"used,attr,omitempty"` +} + +// NetworkElement represents a physical network element, such as a Fabric Interconnect. +type NetworkElement struct { + XMLName xml.Name `xml:"networkElement"` + AdminEvacState string `xml:"adminEvacState,attr,omitempty"` + AdminInbandInterfaceState string `xml:"adminInbandIfState,attr,omitempty"` + ChildAction string `xml:"childAction,attr,omitempty"` + DiffMemory int `xml:"diffMemory,attr,omitempty"` + Dn string `xml:"dn,attr,omitempty"` + ExpectedMemory int `xml:"expectedMemory,attr,omitempty"` + FltAggr int `xml:"fltAggr,attr,omitempty"` + ForceEvac string `xml:"forceEvac,attr,omitempty"` + Id string `xml:"id,attr,omitempty"` + InbandInterfaceGateway net.IP `xml:"inbandIfGw,attr,omitempty"` + InbandInterfaceIp net.IP `xml:"inbandIfIp,attr,omitempty"` + InbandInterfaceNetmask net.IP `xml:"inbandIfMask,attr,omitempty"` + InbandInterfaceVnet int `xml:"inbandIfVnet,attr,omitempty"` + InventoryStatus string `xml:"inventoryStatus,attr,omitempty"` + MinActiveFan int `xml:"minActiveFan,attr,omitempty"` + Model string `xml:"model,attr,omitempty"` + OobInterfaceGateway net.IP `xml:"oobIfGw,attr,omitempty"` + OobInterfaceIp net.IP `xml:"oobIfIp,attr,omitempty"` + OobInterfaceNetmask net.IP `xml:"oobIfMask,attr,omitempty"` + OobInterfaceMac string `xml:"oobIfMac,attr,omitempty"` + OperEvacState string `xml:"operEvacState,attr,omitempty"` + Operability string `xml:"operability,attr,omitempty"` + Revision string `xml:"revision,attr,omitempty"` + Serial string `xml:"serial,attr,omitempty"` + ShutdownFanRemoval string `xml:"shutdownFanRemoveal,attr,omitempty"` + Thermal string `xml:"thermal,attr,omitempty"` + TotalMemory int `xml:"totalMemory,attr,omitempty"` + Vendor string `xml:"vendor,attr,omitempty"` + FanModules []EquipmentFanModule `xml:"equipmentFanModule"` + ManagementController ManagementController `xml:"mgmtController"` + StorageItems []StorageItem `xml:"storageItem"` +} diff --git a/local/go-ucs/version/version.go b/local/go-ucs/version/version.go new file mode 100755 index 0000000..6599ca4 --- /dev/null +++ b/local/go-ucs/version/version.go @@ -0,0 +1,5 @@ +// Package version provides information about the current version of the API library. +package version + +// Version is the version of the Cisco UCS API library. +const Version = "0.1.0"