cloudstack.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cloudstack
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "io"
  19. "os"
  20. "path/filepath"
  21. "github.com/kardianos/osext"
  22. "github.com/xanzy/go-cloudstack/cloudstack"
  23. "gopkg.in/gcfg.v1"
  24. "k8s.io/apimachinery/pkg/types"
  25. cloudprovider "k8s.io/cloud-provider"
  26. "k8s.io/klog"
  27. )
  28. // ProviderName is the name of this cloud provider.
  29. const ProviderName = "cloudstack"
  30. // CSConfig wraps the config for the CloudStack cloud provider.
  31. type CSConfig struct {
  32. Global struct {
  33. APIURL string `gcfg:"api-url"`
  34. APIKey string `gcfg:"api-key"`
  35. SecretKey string `gcfg:"secret-key"`
  36. SSLNoVerify bool `gcfg:"ssl-no-verify"`
  37. ProjectID string `gcfg:"project-id"`
  38. Zone string `gcfg:"zone"`
  39. }
  40. }
  41. // CSCloud is an implementation of Interface for CloudStack.
  42. type CSCloud struct {
  43. client *cloudstack.CloudStackClient
  44. metadata *metadata
  45. projectID string // If non-"", all resources will be created within this project
  46. zone string
  47. }
  48. func init() {
  49. cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
  50. cfg, err := readConfig(config)
  51. if err != nil {
  52. return nil, err
  53. }
  54. return newCSCloud(cfg)
  55. })
  56. }
  57. func readConfig(config io.Reader) (*CSConfig, error) {
  58. cfg := &CSConfig{}
  59. if config == nil {
  60. return cfg, nil
  61. }
  62. if err := gcfg.ReadInto(cfg, config); err != nil {
  63. return nil, fmt.Errorf("could not parse cloud provider config: %v", err)
  64. }
  65. return cfg, nil
  66. }
  67. // newCSCloud creates a new instance of CSCloud.
  68. func newCSCloud(cfg *CSConfig) (*CSCloud, error) {
  69. cs := &CSCloud{
  70. projectID: cfg.Global.ProjectID,
  71. zone: cfg.Global.Zone,
  72. }
  73. exe, err := osext.Executable()
  74. if err != nil {
  75. return nil, fmt.Errorf("cloud not find the service executable: %v", err)
  76. }
  77. // When running the kubelet service it's fine to not specify a config file (or only a
  78. // partial config file) as all needed info can be retrieved anonymously using metadata.
  79. if filepath.Base(exe) == "kubelet" || filepath.Base(exe) == "kubelet.exe" {
  80. // In CloudStack your metadata is always served by the DHCP server.
  81. dhcpServer, err := findDHCPServer()
  82. if err == nil {
  83. klog.V(4).Infof("Found metadata server: %v", dhcpServer)
  84. cs.metadata = &metadata{dhcpServer: dhcpServer, zone: cs.zone}
  85. } else {
  86. klog.Errorf("Error searching metadata server: %v", err)
  87. }
  88. }
  89. if cfg.Global.APIURL != "" && cfg.Global.APIKey != "" && cfg.Global.SecretKey != "" {
  90. cs.client = cloudstack.NewAsyncClient(cfg.Global.APIURL, cfg.Global.APIKey, cfg.Global.SecretKey, !cfg.Global.SSLNoVerify)
  91. }
  92. if cs.client == nil {
  93. if cs.metadata != nil {
  94. klog.V(2).Infof("No API URL, key and secret are provided, so only using metadata!")
  95. } else {
  96. return nil, errors.New("no cloud provider config given")
  97. }
  98. }
  99. return cs, nil
  100. }
  101. var _ cloudprovider.Interface = (*CSCloud)(nil)
  102. var _ cloudprovider.Instances = (*CSCloud)(nil)
  103. var _ cloudprovider.LoadBalancer = (*CSCloud)(nil)
  104. var _ cloudprovider.Zones = (*CSCloud)(nil)
  105. // Initialize passes a Kubernetes clientBuilder interface to the cloud provider
  106. func (cs *CSCloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) {
  107. }
  108. // LoadBalancer returns an implementation of LoadBalancer for CloudStack.
  109. func (cs *CSCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
  110. if cs.client == nil {
  111. return nil, false
  112. }
  113. return cs, true
  114. }
  115. // Instances returns an implementation of Instances for CloudStack.
  116. func (cs *CSCloud) Instances() (cloudprovider.Instances, bool) {
  117. if cs.metadata != nil {
  118. return cs.metadata, true
  119. }
  120. if cs.client == nil {
  121. return nil, false
  122. }
  123. return cs, true
  124. }
  125. // Zones returns an implementation of Zones for CloudStack.
  126. func (cs *CSCloud) Zones() (cloudprovider.Zones, bool) {
  127. if cs.metadata != nil {
  128. return cs.metadata, true
  129. }
  130. if cs.client == nil {
  131. return nil, false
  132. }
  133. return cs, true
  134. }
  135. // Clusters returns an implementation of Clusters for CloudStack.
  136. func (cs *CSCloud) Clusters() (cloudprovider.Clusters, bool) {
  137. if cs.client == nil {
  138. return nil, false
  139. }
  140. return nil, false
  141. }
  142. // Routes returns an implementation of Routes for CloudStack.
  143. func (cs *CSCloud) Routes() (cloudprovider.Routes, bool) {
  144. if cs.client == nil {
  145. return nil, false
  146. }
  147. return nil, false
  148. }
  149. // ProviderName returns the cloud provider ID.
  150. func (cs *CSCloud) ProviderName() string {
  151. return ProviderName
  152. }
  153. // HasClusterID returns true if the cluster has a clusterID
  154. func (cs *CSCloud) HasClusterID() bool {
  155. return true
  156. }
  157. // GetZone returns the Zone containing the region that the program is running in.
  158. func (cs *CSCloud) GetZone(ctx context.Context) (cloudprovider.Zone, error) {
  159. zone := cloudprovider.Zone{}
  160. if cs.zone == "" {
  161. hostname, err := os.Hostname()
  162. if err != nil {
  163. return zone, fmt.Errorf("failed to get hostname for retrieving the zone: %v", err)
  164. }
  165. instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName(hostname)
  166. if err != nil {
  167. if count == 0 {
  168. return zone, fmt.Errorf("could not find instance for retrieving the zone: %v", err)
  169. }
  170. return zone, fmt.Errorf("error getting instance for retrieving the zone: %v", err)
  171. }
  172. cs.zone = instance.Zonename
  173. }
  174. klog.V(2).Infof("Current zone is %v", cs.zone)
  175. zone.FailureDomain = cs.zone
  176. zone.Region = cs.zone
  177. return zone, nil
  178. }
  179. // GetZoneByProviderID returns the Zone, found by using the provider ID.
  180. func (cs *CSCloud) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) {
  181. zone := cloudprovider.Zone{}
  182. instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByID(
  183. providerID,
  184. cloudstack.WithProject(cs.projectID),
  185. )
  186. if err != nil {
  187. if count == 0 {
  188. return zone, fmt.Errorf("could not find node by ID: %v", providerID)
  189. }
  190. return zone, fmt.Errorf("error retrieving zone: %v", err)
  191. }
  192. klog.V(2).Infof("Current zone is %v", cs.zone)
  193. zone.FailureDomain = instance.Zonename
  194. zone.Region = instance.Zonename
  195. return zone, nil
  196. }
  197. // GetZoneByNodeName returns the Zone, found by using the node name.
  198. func (cs *CSCloud) GetZoneByNodeName(ctx context.Context, nodeName types.NodeName) (cloudprovider.Zone, error) {
  199. zone := cloudprovider.Zone{}
  200. instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName(
  201. string(nodeName),
  202. cloudstack.WithProject(cs.projectID),
  203. )
  204. if err != nil {
  205. if count == 0 {
  206. return zone, fmt.Errorf("could not find node: %v", nodeName)
  207. }
  208. return zone, fmt.Errorf("error retrieving zone: %v", err)
  209. }
  210. klog.V(2).Infof("Current zone is %v", cs.zone)
  211. zone.FailureDomain = instance.Zonename
  212. zone.Region = instance.Zonename
  213. return zone, nil
  214. }