//
// Copyright 2018, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package cloudstack

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"sort"
	"strings"
	"time"
)

// UnlimitedResourceID is a special ID to define an unlimited resource
const UnlimitedResourceID = "-1"

var idRegex = regexp.MustCompile(`^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|-1)$`)

// IsID return true if the passed ID is either a UUID or a UnlimitedResourceID
func IsID(id string) bool {
	return idRegex.MatchString(id)
}

// ClientOption can be passed to new client functions to set custom options
type ClientOption func(*CloudStackClient)

// OptionFunc can be passed to the courtesy helper functions to set additional parameters
type OptionFunc func(*CloudStackClient, interface{}) error

type CSError struct {
	ErrorCode   int    `json:"errorcode"`
	CSErrorCode int    `json:"cserrorcode"`
	ErrorText   string `json:"errortext"`
}

func (e *CSError) Error() error {
	return fmt.Errorf("CloudStack API error %d (CSExceptionErrorCode: %d): %s", e.ErrorCode, e.CSErrorCode, e.ErrorText)
}

type CloudStackClient struct {
	HTTPGETOnly bool // If `true` only use HTTP GET calls

	client  *http.Client // The http client for communicating
	baseURL string       // The base URL of the API
	apiKey  string       // Api key
	secret  string       // Secret key
	async   bool         // Wait for async calls to finish
	options []OptionFunc // A list of option functions to apply to all API calls
	timeout int64        // Max waiting timeout in seconds for async jobs to finish; defaults to 300 seconds

	APIDiscovery        *APIDiscoveryService
	Account             *AccountService
	Address             *AddressService
	AffinityGroup       *AffinityGroupService
	Alert               *AlertService
	Asyncjob            *AsyncjobService
	Authentication      *AuthenticationService
	AutoScale           *AutoScaleService
	Baremetal           *BaremetalService
	BigSwitchBCF        *BigSwitchBCFService
	BrocadeVCS          *BrocadeVCSService
	Certificate         *CertificateService
	CloudIdentifier     *CloudIdentifierService
	Cluster             *ClusterService
	Configuration       *ConfigurationService
	Custom              *CustomService
	DiskOffering        *DiskOfferingService
	Domain              *DomainService
	Event               *EventService
	Firewall            *FirewallService
	GuestOS             *GuestOSService
	Host                *HostService
	Hypervisor          *HypervisorService
	ISO                 *ISOService
	ImageStore          *ImageStoreService
	InternalLB          *InternalLBService
	LDAP                *LDAPService
	Limit               *LimitService
	LoadBalancer        *LoadBalancerService
	NAT                 *NATService
	NetworkACL          *NetworkACLService
	NetworkDevice       *NetworkDeviceService
	NetworkOffering     *NetworkOfferingService
	Network             *NetworkService
	Nic                 *NicService
	NiciraNVP           *NiciraNVPService
	NuageVSP            *NuageVSPService
	OutofbandManagement *OutofbandManagementService
	OvsElement          *OvsElementService
	Pod                 *PodService
	Pool                *PoolService
	PortableIP          *PortableIPService
	Project             *ProjectService
	Quota               *QuotaService
	Region              *RegionService
	Resourcemetadata    *ResourcemetadataService
	Resourcetags        *ResourcetagsService
	Role                *RoleService
	Router              *RouterService
	SSH                 *SSHService
	SecurityGroup       *SecurityGroupService
	ServiceOffering     *ServiceOfferingService
	Snapshot            *SnapshotService
	StoragePool         *StoragePoolService
	StratosphereSSP     *StratosphereSSPService
	Swift               *SwiftService
	SystemCapacity      *SystemCapacityService
	SystemVM            *SystemVMService
	Template            *TemplateService
	UCS                 *UCSService
	Usage               *UsageService
	User                *UserService
	VLAN                *VLANService
	VMGroup             *VMGroupService
	VPC                 *VPCService
	VPN                 *VPNService
	VirtualMachine      *VirtualMachineService
	Volume              *VolumeService
	Zone                *ZoneService
}

// Creates a new client for communicating with CloudStack
func newClient(apiurl string, apikey string, secret string, async bool, verifyssl bool, options ...ClientOption) *CloudStackClient {
	jar, _ := cookiejar.New(nil)
	cs := &CloudStackClient{
		client: &http.Client{
			Jar: jar,
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				DialContext: (&net.Dialer{
					Timeout:   30 * time.Second,
					KeepAlive: 30 * time.Second,
					DualStack: true,
				}).DialContext,
				MaxIdleConns:          100,
				IdleConnTimeout:       90 * time.Second,
				TLSClientConfig:       &tls.Config{InsecureSkipVerify: !verifyssl},
				TLSHandshakeTimeout:   10 * time.Second,
				ExpectContinueTimeout: 1 * time.Second,
			},
			Timeout: time.Duration(60 * time.Second),
		},
		baseURL: apiurl,
		apiKey:  apikey,
		secret:  secret,
		async:   async,
		options: []OptionFunc{},
		timeout: 300,
	}

	for _, fn := range options {
		fn(cs)
	}

	cs.APIDiscovery = NewAPIDiscoveryService(cs)
	cs.Account = NewAccountService(cs)
	cs.Address = NewAddressService(cs)
	cs.AffinityGroup = NewAffinityGroupService(cs)
	cs.Alert = NewAlertService(cs)
	cs.Asyncjob = NewAsyncjobService(cs)
	cs.Authentication = NewAuthenticationService(cs)
	cs.AutoScale = NewAutoScaleService(cs)
	cs.Baremetal = NewBaremetalService(cs)
	cs.BigSwitchBCF = NewBigSwitchBCFService(cs)
	cs.BrocadeVCS = NewBrocadeVCSService(cs)
	cs.Certificate = NewCertificateService(cs)
	cs.CloudIdentifier = NewCloudIdentifierService(cs)
	cs.Cluster = NewClusterService(cs)
	cs.Configuration = NewConfigurationService(cs)
	cs.Custom = NewCustomService(cs)
	cs.DiskOffering = NewDiskOfferingService(cs)
	cs.Domain = NewDomainService(cs)
	cs.Event = NewEventService(cs)
	cs.Firewall = NewFirewallService(cs)
	cs.GuestOS = NewGuestOSService(cs)
	cs.Host = NewHostService(cs)
	cs.Hypervisor = NewHypervisorService(cs)
	cs.ISO = NewISOService(cs)
	cs.ImageStore = NewImageStoreService(cs)
	cs.InternalLB = NewInternalLBService(cs)
	cs.LDAP = NewLDAPService(cs)
	cs.Limit = NewLimitService(cs)
	cs.LoadBalancer = NewLoadBalancerService(cs)
	cs.NAT = NewNATService(cs)
	cs.NetworkACL = NewNetworkACLService(cs)
	cs.NetworkDevice = NewNetworkDeviceService(cs)
	cs.NetworkOffering = NewNetworkOfferingService(cs)
	cs.Network = NewNetworkService(cs)
	cs.Nic = NewNicService(cs)
	cs.NiciraNVP = NewNiciraNVPService(cs)
	cs.NuageVSP = NewNuageVSPService(cs)
	cs.OutofbandManagement = NewOutofbandManagementService(cs)
	cs.OvsElement = NewOvsElementService(cs)
	cs.Pod = NewPodService(cs)
	cs.Pool = NewPoolService(cs)
	cs.PortableIP = NewPortableIPService(cs)
	cs.Project = NewProjectService(cs)
	cs.Quota = NewQuotaService(cs)
	cs.Region = NewRegionService(cs)
	cs.Resourcemetadata = NewResourcemetadataService(cs)
	cs.Resourcetags = NewResourcetagsService(cs)
	cs.Role = NewRoleService(cs)
	cs.Router = NewRouterService(cs)
	cs.SSH = NewSSHService(cs)
	cs.SecurityGroup = NewSecurityGroupService(cs)
	cs.ServiceOffering = NewServiceOfferingService(cs)
	cs.Snapshot = NewSnapshotService(cs)
	cs.StoragePool = NewStoragePoolService(cs)
	cs.StratosphereSSP = NewStratosphereSSPService(cs)
	cs.Swift = NewSwiftService(cs)
	cs.SystemCapacity = NewSystemCapacityService(cs)
	cs.SystemVM = NewSystemVMService(cs)
	cs.Template = NewTemplateService(cs)
	cs.UCS = NewUCSService(cs)
	cs.Usage = NewUsageService(cs)
	cs.User = NewUserService(cs)
	cs.VLAN = NewVLANService(cs)
	cs.VMGroup = NewVMGroupService(cs)
	cs.VPC = NewVPCService(cs)
	cs.VPN = NewVPNService(cs)
	cs.VirtualMachine = NewVirtualMachineService(cs)
	cs.Volume = NewVolumeService(cs)
	cs.Zone = NewZoneService(cs)

	return cs
}

// Default non-async client. So for async calls you need to implement and check the async job result yourself. When using
// HTTPS with a self-signed certificate to connect to your CloudStack API, you would probably want to set 'verifyssl' to
// false so the call ignores the SSL errors/warnings.
func NewClient(apiurl string, apikey string, secret string, verifyssl bool, options ...ClientOption) *CloudStackClient {
	cs := newClient(apiurl, apikey, secret, false, verifyssl, options...)
	return cs
}

// For sync API calls this client behaves exactly the same as a standard client call, but for async API calls
// this client will wait until the async job is finished or until the configured AsyncTimeout is reached. When the async
// job finishes successfully it will return actual object received from the API and nil, but when the timout is
// reached it will return the initial object containing the async job ID for the running job and a warning.
func NewAsyncClient(apiurl string, apikey string, secret string, verifyssl bool, options ...ClientOption) *CloudStackClient {
	cs := newClient(apiurl, apikey, secret, true, verifyssl, options...)
	return cs
}

// When using the async client an api call will wait for the async call to finish before returning. The default is to poll for 300 seconds
// seconds, to check if the async job is finished.
func (cs *CloudStackClient) AsyncTimeout(timeoutInSeconds int64) {
	cs.timeout = timeoutInSeconds
}

// Set any default options that would be added to all API calls that support it.
func (cs *CloudStackClient) DefaultOptions(options ...OptionFunc) {
	if options != nil {
		cs.options = options
	} else {
		cs.options = []OptionFunc{}
	}
}

var AsyncTimeoutErr = errors.New("Timeout while waiting for async job to finish")

// A helper function that you can use to get the result of a running async job. If the job is not finished within the configured
// timeout, the async job returns a AsyncTimeoutErr.
func (cs *CloudStackClient) GetAsyncJobResult(jobid string, timeout int64) (json.RawMessage, error) {
	var timer time.Duration
	currentTime := time.Now().Unix()

	for {
		p := cs.Asyncjob.NewQueryAsyncJobResultParams(jobid)
		r, err := cs.Asyncjob.QueryAsyncJobResult(p)
		if err != nil {
			return nil, err
		}

		// Status 1 means the job is finished successfully
		if r.Jobstatus == 1 {
			return r.Jobresult, nil
		}

		// When the status is 2, the job has failed
		if r.Jobstatus == 2 {
			if r.Jobresulttype == "text" {
				return nil, fmt.Errorf(string(r.Jobresult))
			} else {
				return nil, fmt.Errorf("Undefined error: %s", string(r.Jobresult))
			}
		}

		if time.Now().Unix()-currentTime > timeout {
			return nil, AsyncTimeoutErr
		}

		// Add an (extremely simple) exponential backoff like feature to prevent
		// flooding the CloudStack API
		if timer < 15 {
			timer++
		}

		time.Sleep(timer * time.Second)
	}
}

// Execute the request against a CS API. Will return the raw JSON data returned by the API and nil if
// no error occured. If the API returns an error the result will be nil and the HTTP error code and CS
// error details. If a processing (code) error occurs the result will be nil and the generated error
func (cs *CloudStackClient) newRequest(api string, params url.Values) (json.RawMessage, error) {
	params.Set("apiKey", cs.apiKey)
	params.Set("command", api)
	params.Set("response", "json")

	// Generate signature for API call
	// * Serialize parameters, URL encoding only values and sort them by key, done by encodeValues
	// * Convert the entire argument string to lowercase
	// * Replace all instances of '+' to '%20'
	// * Calculate HMAC SHA1 of argument string with CloudStack secret
	// * URL encode the string and convert to base64
	s := encodeValues(params)
	s2 := strings.ToLower(s)
	s3 := strings.Replace(s2, "+", "%20", -1)
	mac := hmac.New(sha1.New, []byte(cs.secret))
	mac.Write([]byte(s3))
	signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))

	var err error
	var resp *http.Response
	if !cs.HTTPGETOnly && (api == "deployVirtualMachine" || api == "login" || api == "updateVirtualMachine") {
		// The deployVirtualMachine API should be called using a POST call
		// so we don't have to worry about the userdata size

		// Add the unescaped signature to the POST params
		params.Set("signature", signature)

		// Make a POST call
		resp, err = cs.client.PostForm(cs.baseURL, params)
	} else {
		// Create the final URL before we issue the request
		url := cs.baseURL + "?" + s + "&signature=" + url.QueryEscape(signature)

		// Make a GET call
		resp, err = cs.client.Get(url)
	}
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Need to get the raw value to make the result play nice
	b, err = getRawValue(b)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		var e CSError
		if err := json.Unmarshal(b, &e); err != nil {
			return nil, err
		}
		return nil, e.Error()
	}
	return b, nil
}

// Custom version of net/url Encode that only URL escapes values
// Unmodified portions here remain under BSD license of The Go Authors: https://go.googlesource.com/go/+/master/LICENSE
func encodeValues(v url.Values) string {
	if v == nil {
		return ""
	}
	var buf bytes.Buffer
	keys := make([]string, 0, len(v))
	for k := range v {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		vs := v[k]
		prefix := k + "="
		for _, v := range vs {
			if buf.Len() > 0 {
				buf.WriteByte('&')
			}
			buf.WriteString(prefix)
			buf.WriteString(url.QueryEscape(v))
		}
	}
	return buf.String()
}

// Generic function to get the first raw value from a response as json.RawMessage
func getRawValue(b json.RawMessage) (json.RawMessage, error) {
	var m map[string]json.RawMessage
	if err := json.Unmarshal(b, &m); err != nil {
		return nil, err
	}
	for _, v := range m {
		return v, nil
	}
	return nil, fmt.Errorf("Unable to extract the raw value from:\n\n%s\n\n", string(b))
}

// getSortedKeysFromMap returns the keys from m in increasing order.
func getSortedKeysFromMap(m map[string]string) (keys []string) {
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return
}

// WithAsyncTimeout takes a custom timeout to be used by the CloudStackClient
func WithAsyncTimeout(timeout int64) ClientOption {
	return func(cs *CloudStackClient) {
		if timeout != 0 {
			cs.timeout = timeout
		}
	}
}

// DomainIDSetter is an interface that every type that can set a domain ID must implement
type DomainIDSetter interface {
	SetDomainid(string)
}

// WithDomain takes either a domain name or ID and sets the `domainid` parameter
func WithDomain(domain string) OptionFunc {
	return func(cs *CloudStackClient, p interface{}) error {
		ps, ok := p.(DomainIDSetter)

		if !ok || domain == "" {
			return nil
		}

		if !IsID(domain) {
			id, _, err := cs.Domain.GetDomainID(domain)
			if err != nil {
				return err
			}
			domain = id
		}

		ps.SetDomainid(domain)

		return nil
	}
}

// WithHTTPClient takes a custom HTTP client to be used by the CloudStackClient
func WithHTTPClient(client *http.Client) ClientOption {
	return func(cs *CloudStackClient) {
		if client != nil {
			if client.Jar == nil {
				client.Jar = cs.client.Jar
			}
			cs.client = client
		}
	}
}

// ProjectIDSetter is an interface that every type that can set a project ID must implement
type ProjectIDSetter interface {
	SetProjectid(string)
}

// WithProject takes either a project name or ID and sets the `projectid` parameter
func WithProject(project string) OptionFunc {
	return func(cs *CloudStackClient, p interface{}) error {
		ps, ok := p.(ProjectIDSetter)

		if !ok || project == "" {
			return nil
		}

		if !IsID(project) {
			id, _, err := cs.Project.GetProjectID(project)
			if err != nil {
				return err
			}
			project = id
		}

		ps.SetProjectid(project)

		return nil
	}
}

// VPCIDSetter is an interface that every type that can set a vpc ID must implement
type VPCIDSetter interface {
	SetVpcid(string)
}

// WithVPCID takes a vpc ID and sets the `vpcid` parameter
func WithVPCID(id string) OptionFunc {
	return func(cs *CloudStackClient, p interface{}) error {
		vs, ok := p.(VPCIDSetter)

		if !ok || id == "" {
			return nil
		}

		vs.SetVpcid(id)

		return nil
	}
}

// ZoneIDSetter is an interface that every type that can set a zone ID must implement
type ZoneIDSetter interface {
	SetZoneid(string)
}

// WithZone takes either a zone name or ID and sets the `zoneid` parameter
func WithZone(zone string) OptionFunc {
	return func(cs *CloudStackClient, p interface{}) error {
		zs, ok := p.(ZoneIDSetter)

		if !ok || zone == "" {
			return nil
		}

		if !IsID(zone) {
			id, _, err := cs.Zone.GetZoneID(zone)
			if err != nil {
				return err
			}
			zone = id
		}

		zs.SetZoneid(zone)

		return nil
	}
}

type APIDiscoveryService struct {
	cs *CloudStackClient
}

func NewAPIDiscoveryService(cs *CloudStackClient) *APIDiscoveryService {
	return &APIDiscoveryService{cs: cs}
}

type AccountService struct {
	cs *CloudStackClient
}

func NewAccountService(cs *CloudStackClient) *AccountService {
	return &AccountService{cs: cs}
}

type AddressService struct {
	cs *CloudStackClient
}

func NewAddressService(cs *CloudStackClient) *AddressService {
	return &AddressService{cs: cs}
}

type AffinityGroupService struct {
	cs *CloudStackClient
}

func NewAffinityGroupService(cs *CloudStackClient) *AffinityGroupService {
	return &AffinityGroupService{cs: cs}
}

type AlertService struct {
	cs *CloudStackClient
}

func NewAlertService(cs *CloudStackClient) *AlertService {
	return &AlertService{cs: cs}
}

type AsyncjobService struct {
	cs *CloudStackClient
}

func NewAsyncjobService(cs *CloudStackClient) *AsyncjobService {
	return &AsyncjobService{cs: cs}
}

type AuthenticationService struct {
	cs *CloudStackClient
}

func NewAuthenticationService(cs *CloudStackClient) *AuthenticationService {
	return &AuthenticationService{cs: cs}
}

type AutoScaleService struct {
	cs *CloudStackClient
}

func NewAutoScaleService(cs *CloudStackClient) *AutoScaleService {
	return &AutoScaleService{cs: cs}
}

type BaremetalService struct {
	cs *CloudStackClient
}

func NewBaremetalService(cs *CloudStackClient) *BaremetalService {
	return &BaremetalService{cs: cs}
}

type BigSwitchBCFService struct {
	cs *CloudStackClient
}

func NewBigSwitchBCFService(cs *CloudStackClient) *BigSwitchBCFService {
	return &BigSwitchBCFService{cs: cs}
}

type BrocadeVCSService struct {
	cs *CloudStackClient
}

func NewBrocadeVCSService(cs *CloudStackClient) *BrocadeVCSService {
	return &BrocadeVCSService{cs: cs}
}

type CertificateService struct {
	cs *CloudStackClient
}

func NewCertificateService(cs *CloudStackClient) *CertificateService {
	return &CertificateService{cs: cs}
}

type CloudIdentifierService struct {
	cs *CloudStackClient
}

func NewCloudIdentifierService(cs *CloudStackClient) *CloudIdentifierService {
	return &CloudIdentifierService{cs: cs}
}

type ClusterService struct {
	cs *CloudStackClient
}

func NewClusterService(cs *CloudStackClient) *ClusterService {
	return &ClusterService{cs: cs}
}

type ConfigurationService struct {
	cs *CloudStackClient
}

func NewConfigurationService(cs *CloudStackClient) *ConfigurationService {
	return &ConfigurationService{cs: cs}
}

type CustomService struct {
	cs *CloudStackClient
}

func NewCustomService(cs *CloudStackClient) *CustomService {
	return &CustomService{cs: cs}
}

type DiskOfferingService struct {
	cs *CloudStackClient
}

func NewDiskOfferingService(cs *CloudStackClient) *DiskOfferingService {
	return &DiskOfferingService{cs: cs}
}

type DomainService struct {
	cs *CloudStackClient
}

func NewDomainService(cs *CloudStackClient) *DomainService {
	return &DomainService{cs: cs}
}

type EventService struct {
	cs *CloudStackClient
}

func NewEventService(cs *CloudStackClient) *EventService {
	return &EventService{cs: cs}
}

type FirewallService struct {
	cs *CloudStackClient
}

func NewFirewallService(cs *CloudStackClient) *FirewallService {
	return &FirewallService{cs: cs}
}

type GuestOSService struct {
	cs *CloudStackClient
}

func NewGuestOSService(cs *CloudStackClient) *GuestOSService {
	return &GuestOSService{cs: cs}
}

type HostService struct {
	cs *CloudStackClient
}

func NewHostService(cs *CloudStackClient) *HostService {
	return &HostService{cs: cs}
}

type HypervisorService struct {
	cs *CloudStackClient
}

func NewHypervisorService(cs *CloudStackClient) *HypervisorService {
	return &HypervisorService{cs: cs}
}

type ISOService struct {
	cs *CloudStackClient
}

func NewISOService(cs *CloudStackClient) *ISOService {
	return &ISOService{cs: cs}
}

type ImageStoreService struct {
	cs *CloudStackClient
}

func NewImageStoreService(cs *CloudStackClient) *ImageStoreService {
	return &ImageStoreService{cs: cs}
}

type InternalLBService struct {
	cs *CloudStackClient
}

func NewInternalLBService(cs *CloudStackClient) *InternalLBService {
	return &InternalLBService{cs: cs}
}

type LDAPService struct {
	cs *CloudStackClient
}

func NewLDAPService(cs *CloudStackClient) *LDAPService {
	return &LDAPService{cs: cs}
}

type LimitService struct {
	cs *CloudStackClient
}

func NewLimitService(cs *CloudStackClient) *LimitService {
	return &LimitService{cs: cs}
}

type LoadBalancerService struct {
	cs *CloudStackClient
}

func NewLoadBalancerService(cs *CloudStackClient) *LoadBalancerService {
	return &LoadBalancerService{cs: cs}
}

type NATService struct {
	cs *CloudStackClient
}

func NewNATService(cs *CloudStackClient) *NATService {
	return &NATService{cs: cs}
}

type NetworkACLService struct {
	cs *CloudStackClient
}

func NewNetworkACLService(cs *CloudStackClient) *NetworkACLService {
	return &NetworkACLService{cs: cs}
}

type NetworkDeviceService struct {
	cs *CloudStackClient
}

func NewNetworkDeviceService(cs *CloudStackClient) *NetworkDeviceService {
	return &NetworkDeviceService{cs: cs}
}

type NetworkOfferingService struct {
	cs *CloudStackClient
}

func NewNetworkOfferingService(cs *CloudStackClient) *NetworkOfferingService {
	return &NetworkOfferingService{cs: cs}
}

type NetworkService struct {
	cs *CloudStackClient
}

func NewNetworkService(cs *CloudStackClient) *NetworkService {
	return &NetworkService{cs: cs}
}

type NicService struct {
	cs *CloudStackClient
}

func NewNicService(cs *CloudStackClient) *NicService {
	return &NicService{cs: cs}
}

type NiciraNVPService struct {
	cs *CloudStackClient
}

func NewNiciraNVPService(cs *CloudStackClient) *NiciraNVPService {
	return &NiciraNVPService{cs: cs}
}

type NuageVSPService struct {
	cs *CloudStackClient
}

func NewNuageVSPService(cs *CloudStackClient) *NuageVSPService {
	return &NuageVSPService{cs: cs}
}

type OutofbandManagementService struct {
	cs *CloudStackClient
}

func NewOutofbandManagementService(cs *CloudStackClient) *OutofbandManagementService {
	return &OutofbandManagementService{cs: cs}
}

type OvsElementService struct {
	cs *CloudStackClient
}

func NewOvsElementService(cs *CloudStackClient) *OvsElementService {
	return &OvsElementService{cs: cs}
}

type PodService struct {
	cs *CloudStackClient
}

func NewPodService(cs *CloudStackClient) *PodService {
	return &PodService{cs: cs}
}

type PoolService struct {
	cs *CloudStackClient
}

func NewPoolService(cs *CloudStackClient) *PoolService {
	return &PoolService{cs: cs}
}

type PortableIPService struct {
	cs *CloudStackClient
}

func NewPortableIPService(cs *CloudStackClient) *PortableIPService {
	return &PortableIPService{cs: cs}
}

type ProjectService struct {
	cs *CloudStackClient
}

func NewProjectService(cs *CloudStackClient) *ProjectService {
	return &ProjectService{cs: cs}
}

type QuotaService struct {
	cs *CloudStackClient
}

func NewQuotaService(cs *CloudStackClient) *QuotaService {
	return &QuotaService{cs: cs}
}

type RegionService struct {
	cs *CloudStackClient
}

func NewRegionService(cs *CloudStackClient) *RegionService {
	return &RegionService{cs: cs}
}

type ResourcemetadataService struct {
	cs *CloudStackClient
}

func NewResourcemetadataService(cs *CloudStackClient) *ResourcemetadataService {
	return &ResourcemetadataService{cs: cs}
}

type ResourcetagsService struct {
	cs *CloudStackClient
}

func NewResourcetagsService(cs *CloudStackClient) *ResourcetagsService {
	return &ResourcetagsService{cs: cs}
}

type RoleService struct {
	cs *CloudStackClient
}

func NewRoleService(cs *CloudStackClient) *RoleService {
	return &RoleService{cs: cs}
}

type RouterService struct {
	cs *CloudStackClient
}

func NewRouterService(cs *CloudStackClient) *RouterService {
	return &RouterService{cs: cs}
}

type SSHService struct {
	cs *CloudStackClient
}

func NewSSHService(cs *CloudStackClient) *SSHService {
	return &SSHService{cs: cs}
}

type SecurityGroupService struct {
	cs *CloudStackClient
}

func NewSecurityGroupService(cs *CloudStackClient) *SecurityGroupService {
	return &SecurityGroupService{cs: cs}
}

type ServiceOfferingService struct {
	cs *CloudStackClient
}

func NewServiceOfferingService(cs *CloudStackClient) *ServiceOfferingService {
	return &ServiceOfferingService{cs: cs}
}

type SnapshotService struct {
	cs *CloudStackClient
}

func NewSnapshotService(cs *CloudStackClient) *SnapshotService {
	return &SnapshotService{cs: cs}
}

type StoragePoolService struct {
	cs *CloudStackClient
}

func NewStoragePoolService(cs *CloudStackClient) *StoragePoolService {
	return &StoragePoolService{cs: cs}
}

type StratosphereSSPService struct {
	cs *CloudStackClient
}

func NewStratosphereSSPService(cs *CloudStackClient) *StratosphereSSPService {
	return &StratosphereSSPService{cs: cs}
}

type SwiftService struct {
	cs *CloudStackClient
}

func NewSwiftService(cs *CloudStackClient) *SwiftService {
	return &SwiftService{cs: cs}
}

type SystemCapacityService struct {
	cs *CloudStackClient
}

func NewSystemCapacityService(cs *CloudStackClient) *SystemCapacityService {
	return &SystemCapacityService{cs: cs}
}

type SystemVMService struct {
	cs *CloudStackClient
}

func NewSystemVMService(cs *CloudStackClient) *SystemVMService {
	return &SystemVMService{cs: cs}
}

type TemplateService struct {
	cs *CloudStackClient
}

func NewTemplateService(cs *CloudStackClient) *TemplateService {
	return &TemplateService{cs: cs}
}

type UCSService struct {
	cs *CloudStackClient
}

func NewUCSService(cs *CloudStackClient) *UCSService {
	return &UCSService{cs: cs}
}

type UsageService struct {
	cs *CloudStackClient
}

func NewUsageService(cs *CloudStackClient) *UsageService {
	return &UsageService{cs: cs}
}

type UserService struct {
	cs *CloudStackClient
}

func NewUserService(cs *CloudStackClient) *UserService {
	return &UserService{cs: cs}
}

type VLANService struct {
	cs *CloudStackClient
}

func NewVLANService(cs *CloudStackClient) *VLANService {
	return &VLANService{cs: cs}
}

type VMGroupService struct {
	cs *CloudStackClient
}

func NewVMGroupService(cs *CloudStackClient) *VMGroupService {
	return &VMGroupService{cs: cs}
}

type VPCService struct {
	cs *CloudStackClient
}

func NewVPCService(cs *CloudStackClient) *VPCService {
	return &VPCService{cs: cs}
}

type VPNService struct {
	cs *CloudStackClient
}

func NewVPNService(cs *CloudStackClient) *VPNService {
	return &VPNService{cs: cs}
}

type VirtualMachineService struct {
	cs *CloudStackClient
}

func NewVirtualMachineService(cs *CloudStackClient) *VirtualMachineService {
	return &VirtualMachineService{cs: cs}
}

type VolumeService struct {
	cs *CloudStackClient
}

func NewVolumeService(cs *CloudStackClient) *VolumeService {
	return &VolumeService{cs: cs}
}

type ZoneService struct {
	cs *CloudStackClient
}

func NewZoneService(cs *CloudStackClient) *ZoneService {
	return &ZoneService{cs: cs}
}
