Skip to content

Commit

Permalink
Add a client for Virtual Network management
Browse files Browse the repository at this point in the history
This commit adds entities to generate the (unavailable) XML schema for
virtual network specification, and a client with Get and Set operations
for the currently active subscription.

The pattern for using the client is the same as the powershell version -
get the running config, merge in any desired changes and set the
configuration to the new state. There is no documentation on what the
concurrency constraints are with the underlying Azure REST API, I
suspect it is not safe to call concurrently though can't verify this.
  • Loading branch information
jen20 committed Feb 4, 2015
1 parent af6985d commit c95e423
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 18 deletions.
81 changes: 81 additions & 0 deletions clients/vnetClient/entities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package vnetClient

import (
"encoding/xml"
)

const xmlNamespace = "http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration"
const xmlNamespaceXsd = "http://www.w3.org/2001/XMLSchema"
const xmlNamespaceXsi = "http://www.w3.org/2001/XMLSchema-instance"

//NetworkConfiguration represents the network configuration for an entire Azure
//subscription. TODO: Nicer builder methods for these that abstract away the
//underlying structure
type NetworkConfiguration struct {
XMLName xml.Name `xml:"NetworkConfiguration"`
XmlNamespaceXsd string `xml:"xmlns:xsd,attr"`
XmlNamespaceXsi string `xml:"xmlns:xsi,attr"`
Xmlns string `xml:"xmlns,attr"`
Configuration VirtualNetworkConfiguration `xml:"VirtualNetworkConfiguration"`
}

//NewNetworkConfiguration creates a new empty NetworkConfiguration structure for
//further configuration. The XML namespaces are set correctly.
func NewNetworkConfiguration() NetworkConfiguration {
networkConfiguration := NetworkConfiguration{}
networkConfiguration.setXmlNamespaces()
return networkConfiguration
}

//setXmlNamespaces ensure that all of the required namespaces are set. It should
//be called prior to marshalling the structure to XML for use with the Azure REST
//endpoint. It is used internally prior to submitting requests, but since it is
//idempotent there is no harm in repeat calls.
func (self *NetworkConfiguration) setXmlNamespaces() {
self.XmlNamespaceXsd = xmlNamespaceXsd
self.XmlNamespaceXsi = xmlNamespaceXsi
self.Xmlns = xmlNamespace
}

type VirtualNetworkConfiguration struct {
Dns Dns `xml:"Dns,omitempty"`
LocalNetworkSites []LocalNetworkSite `xml:"LocalNetworkSites>LocalNetworkSite"`
VirtualNetworkSites []VirtualNetworkSite `xml:"VirtualNetworkSites>VirtualNetworkSite"`
}

type Dns struct {
DnsServers []DnsServer `xml:"DnsServers,omitempty>DnsServer,omitempty"`
}

type DnsServer struct {
XMLName xml.Name `xml:"DnsServer"`
Name string `xml:"name,attr"`
IPAddress string `xml:"IPAddress,attr"`
}

type DnsServerRef struct {
Name string `xml:"name,attr"`
}

type VirtualNetworkSite struct {
Name string `xml:"name,attr"`
Location string `xml:"Location,attr"`
AddressSpace AddressSpace `xml:"AddressSpace"`
Subnets []Subnet `xml:"Subnets>Subnet"`
DnsServersRef []DnsServerRef `xml:"DnsServersRef,omitempty>DnsServerRef"`
}

type LocalNetworkSite struct {
Name string `xml:"name,attr"`
VPNGatewayAddress string
AddressSpace AddressSpace
}

type AddressSpace struct {
AddressPrefix []string
}

type Subnet struct {
Name string `xml:"name,attr"`
AddressPrefix string
}
49 changes: 49 additions & 0 deletions clients/vnetClient/vnetClient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package vnetClient

import (
"encoding/xml"
azure "github.com/MSOpenTech/azure-sdk-for-go"
)

const (
azureNetworkConfigurationURL = "services/networking/media"
)

//GetVirtualNetworkConfiguration retreives the current virtual network
//configuration for the currently active subscription. Note that the
//underlying Azure API means that network related operations are not safe
//for running concurrently.
func GetVirtualNetworkConfiguration() (NetworkConfiguration, error) {
networkConfiguration := NewNetworkConfiguration()
response, err := azure.SendAzureGetRequest(azureNetworkConfigurationURL)
if err != nil {
return networkConfiguration, err
}

err = xml.Unmarshal(response, &networkConfiguration)
if err != nil {
return networkConfiguration, err
}

return networkConfiguration, nil
}

//SetVirtualNetworkConfiguration configures the virtual networks for the
//currently active subscription according to the NetworkConfiguration given.
//Note that the underlying Azure API means that network related operations
//are not safe for running concurrently.
func SetVirtualNetworkConfiguration(networkConfiguration NetworkConfiguration) error {
networkConfiguration.setXmlNamespaces()
networkConfigurationBytes, err := xml.Marshal(networkConfiguration)
if err != nil {
return err
}

requestId, err := azure.SendAzurePutRequest(azureNetworkConfigurationURL, "text/plain", networkConfigurationBytes)
if err != nil {
return err
}

err = azure.WaitAsyncOperation(requestId)
return err
}
54 changes: 36 additions & 18 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import (
const (
paramNotSpecifiedError = "Parameter %s is not specified."

azureManagementDnsName = "https://management.core.windows.net"
msVersionHeader = "x-ms-version"
msVersionHeaderValue = "2014-05-01"
contentHeader = "Content-Type"
contentHeaderValue = "application/xml"
requestIdHeader = "X-Ms-Request-Id"
azureManagementDnsName = "https://management.core.windows.net"
msVersionHeader = "x-ms-version"
msVersionHeaderValue = "2014-05-01"
contentHeader = "Content-Type"
defaultContentHeaderValue = "application/xml"
requestIdHeader = "X-Ms-Request-Id"
)

//Region public methods starts
Expand All @@ -32,7 +32,7 @@ func SendAzureGetRequest(url string) ([]byte, error) {
return nil, fmt.Errorf(paramNotSpecifiedError, "url")
}

response, err := SendAzureRequest(url, "GET", nil)
response, err := SendAzureRequest(url, "GET", "", nil)
if err != nil {
return nil, err
}
Expand All @@ -46,7 +46,21 @@ func SendAzurePostRequest(url string, data []byte) (string, error) {
return "", fmt.Errorf(paramNotSpecifiedError, "url")
}

response, err := SendAzureRequest(url, "POST", data)
response, err := SendAzureRequest(url, "POST", "", data)
if err != nil {
return "", err
}

requestId := response.Header[requestIdHeader]
return requestId[0], nil
}

func SendAzurePutRequest(url string, contentType string, data []byte) (string, error) {
if len(url) == 0 {
return "", fmt.Errorf(paramNotSpecifiedError, contentType, "url")
}

response, err := SendAzureRequest(url, "PUT", contentType, data)
if err != nil {
return "", err
}
Expand All @@ -60,7 +74,7 @@ func SendAzureDeleteRequest(url string) (string, error) {
return "", fmt.Errorf(paramNotSpecifiedError, "url")
}

response, err := SendAzureRequest(url, "DELETE", nil)
response, err := SendAzureRequest(url, "DELETE", "", nil)
if err != nil {
return "", err
}
Expand All @@ -69,7 +83,7 @@ func SendAzureDeleteRequest(url string) (string, error) {
return requestId[0], nil
}

func SendAzureRequest(url string, requestType string, data []byte) (*http.Response, error) {
func SendAzureRequest(url string, requestType string, contentType string, data []byte) (*http.Response, error) {
if len(url) == 0 {
return nil, fmt.Errorf(paramNotSpecifiedError, "url")
}
Expand All @@ -79,7 +93,7 @@ func SendAzureRequest(url string, requestType string, data []byte) (*http.Respon

client := createHttpClient()

response, err := sendRequest(client, url, requestType, data, 7)
response, err := sendRequest(client, url, requestType, contentType, data, 7)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -159,7 +173,7 @@ func CheckStringParams(url string) ([]byte, error) {
return nil, fmt.Errorf(paramNotSpecifiedError, "url")
}

response, err := SendAzureRequest(url, "GET", nil)
response, err := SendAzureRequest(url, "GET", "", nil)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -188,8 +202,8 @@ func NewUUID() (string, error) {

//Region private methods starts

func sendRequest(client *http.Client, url string, requestType string, data []byte, numberOfRetries int) (*http.Response, error) {
request, reqErr := createAzureRequest(url, requestType, data)
func sendRequest(client *http.Client, url string, requestType string, contentType string, data []byte, numberOfRetries int) (*http.Response, error) {
request, reqErr := createAzureRequest(url, requestType, contentType, data)
if reqErr != nil {
return nil, reqErr
}
Expand All @@ -200,7 +214,7 @@ func sendRequest(client *http.Client, url string, requestType string, data []byt
return nil, err
}

return sendRequest(client, url, requestType, data, numberOfRetries-1)
return sendRequest(client, url, requestType, contentType, data, numberOfRetries-1)
}

if response.StatusCode > 299 {
Expand All @@ -211,7 +225,7 @@ func sendRequest(client *http.Client, url string, requestType string, data []byt
return nil, azureErr
}

return sendRequest(client, url, requestType, data, numberOfRetries-1)
return sendRequest(client, url, requestType, contentType, data, numberOfRetries-1)
}
}

Expand All @@ -228,7 +242,7 @@ func getAzureError(responseBody []byte) error {
return error
}

func createAzureRequest(url string, requestType string, data []byte) (*http.Request, error) {
func createAzureRequest(url string, requestType string, contentType string, data []byte) (*http.Request, error) {
var request *http.Request
var err error

Expand All @@ -245,7 +259,11 @@ func createAzureRequest(url string, requestType string, data []byte) (*http.Requ
}

request.Header.Add(msVersionHeader, msVersionHeaderValue)
request.Header.Add(contentHeader, contentHeaderValue)
if len(contentType) > 0 {
request.Header.Add(contentHeader, contentType)
} else {
request.Header.Add(contentHeader, defaultContentHeaderValue)
}

return request, nil
}
Expand Down

0 comments on commit c95e423

Please sign in to comment.