123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735 |
- /*
- Copyright 2016 The Kubernetes Authors.
- 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.
- */
- // This version of Photon cloud provider supports the disk interface
- // for Photon persistent disk volume plugin. LoadBalancer, Routes, and
- // Zones are currently not supported.
- // The use of Photon cloud provider requires to start kubelet, kube-apiserver,
- // and kube-controller-manager with config flag: '--cloud-provider=photon
- // --cloud-config=[path_to_config_file]'. When running multi-node kubernetes
- // using docker, the config file should be located inside /etc/kubernetes.
- package photon
- import (
- "bufio"
- "context"
- "errors"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "strings"
- "github.com/vmware/photon-controller-go-sdk/photon"
- "gopkg.in/gcfg.v1"
- "k8s.io/api/core/v1"
- k8stypes "k8s.io/apimachinery/pkg/types"
- cloudprovider "k8s.io/cloud-provider"
- nodehelpers "k8s.io/cloud-provider/node/helpers"
- "k8s.io/klog"
- )
- const (
- ProviderName = "photon"
- DiskSpecKind = "persistent-disk"
- MAC_OUI_VC = "00:50:56"
- MAC_OUI_ESX = "00:0c:29"
- )
- // overrideIP indicates if the hostname is overridden by IP address, such as when
- // running multi-node kubernetes using docker. In this case the user should set
- // overrideIP = true in cloud config file. Default value is false.
- var overrideIP = false
- var _ cloudprovider.Interface = (*PCCloud)(nil)
- var _ cloudprovider.Instances = (*PCCloud)(nil)
- var _ cloudprovider.Zones = (*PCCloud)(nil)
- // PCCloud is an implementation of the cloud provider interface for Photon Controller.
- type PCCloud struct {
- cfg *PCConfig
- // InstanceID of the server where this PCCloud object is instantiated.
- localInstanceID string
- // local $HOSTNAME
- localHostname string
- // hostname from K8S, could be overridden
- localK8sHostname string
- // Photon project ID. We assume that there is only one Photon Controller project
- // in the environment per current Photon Controller deployment methodology.
- projID string
- cloudprovider.Zone
- photonClient *photon.Client
- logger *log.Logger
- }
- type PCConfig struct {
- Global struct {
- // the Photon Controller endpoint IP address
- CloudTarget string `gcfg:"target"`
- // Photon Controller project name
- Project string `gcfg:"project"`
- // when kubelet is started with '--hostname-override=${IP_ADDRESS}', set to true;
- // otherwise, set to false.
- OverrideIP bool `gcfg:"overrideIP"`
- // VM ID for this node
- VMID string `gcfg:"vmID"`
- // Authentication enabled or not
- AuthEnabled bool `gcfg:"authentication"`
- }
- }
- // Disks is interface for manipulation with PhotonController Persistent Disks.
- type Disks interface {
- // AttachDisk attaches given disk to given node. Current node
- // is used when nodeName is empty string.
- AttachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error
- // DetachDisk detaches given disk to given node. Current node
- // is used when nodeName is empty string.
- DetachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error
- // DiskIsAttached checks if a disk is attached to the given node.
- DiskIsAttached(ctx context.Context, pdID string, nodeName k8stypes.NodeName) (bool, error)
- // DisksAreAttached is a batch function to check if a list of disks are attached
- // to the node with the specified NodeName.
- DisksAreAttached(ctx context.Context, pdIDs []string, nodeName k8stypes.NodeName) (map[string]bool, error)
- // CreateDisk creates a new PD with given properties.
- CreateDisk(volumeOptions *VolumeOptions) (pdID string, err error)
- // DeleteDisk deletes PD.
- DeleteDisk(pdID string) error
- }
- // VolumeOptions specifies capacity, tags, name and flavorID for a volume.
- type VolumeOptions struct {
- CapacityGB int
- Tags map[string]string
- Name string
- Flavor string
- }
- func readConfig(config io.Reader) (PCConfig, error) {
- if config == nil {
- err := fmt.Errorf("cloud provider config file is missing. Please restart kubelet with --cloud-provider=photon --cloud-config=[path_to_config_file]")
- return PCConfig{}, err
- }
- var cfg PCConfig
- err := gcfg.ReadInto(&cfg, config)
- return cfg, err
- }
- func init() {
- cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
- cfg, err := readConfig(config)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: failed to read in cloud provider config file. Error[%v]", err)
- return nil, err
- }
- return newPCCloud(cfg)
- })
- }
- // Retrieve the Photon VM ID from the Photon Controller endpoint based on the node name
- func getVMIDbyNodename(pc *PCCloud, nodeName string) (string, error) {
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for getVMIDbyNodename, error: [%v]", err)
- return "", err
- }
- vmList, err := photonClient.Projects.GetVMs(pc.projID, nil)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to GetVMs from project %s with nodeName %s, error: [%v]", pc.projID, nodeName, err)
- return "", err
- }
- for _, vm := range vmList.Items {
- if vm.Name == nodeName {
- return vm.ID, nil
- }
- }
- return "", fmt.Errorf("No matching started VM is found with name %s", nodeName)
- }
- // Retrieve the Photon VM ID from the Photon Controller endpoint based on the IP address
- func getVMIDbyIP(pc *PCCloud, IPAddress string) (string, error) {
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for getVMIDbyNodename, error: [%v]", err)
- return "", err
- }
- vmList, err := photonClient.Projects.GetVMs(pc.projID, nil)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to GetVMs for project %s. error: [%v]", pc.projID, err)
- return "", err
- }
- for _, vm := range vmList.Items {
- task, err := photonClient.VMs.GetNetworks(vm.ID)
- if err != nil {
- klog.Warningf("Photon Cloud Provider: GetNetworks failed for vm.ID %s, error [%v]", vm.ID, err)
- } else {
- task, err = photonClient.Tasks.Wait(task.ID)
- if err != nil {
- klog.Warningf("Photon Cloud Provider: Wait task for GetNetworks failed for vm.ID %s, error [%v]", vm.ID, err)
- } else {
- networkConnections := task.ResourceProperties.(map[string]interface{})
- networks := networkConnections["networkConnections"].([]interface{})
- for _, nt := range networks {
- network := nt.(map[string]interface{})
- if val, ok := network["ipAddress"]; ok && val != nil {
- ipAddr := val.(string)
- if ipAddr == IPAddress {
- return vm.ID, nil
- }
- }
- }
- }
- }
- }
- return "", fmt.Errorf("No matching VM is found with IP %s", IPAddress)
- }
- func getPhotonClient(pc *PCCloud) (*photon.Client, error) {
- var err error
- if len(pc.cfg.Global.CloudTarget) == 0 {
- return nil, fmt.Errorf("Photon Controller endpoint was not specified")
- }
- options := &photon.ClientOptions{
- IgnoreCertificate: true,
- }
- pc.photonClient = photon.NewClient(pc.cfg.Global.CloudTarget, options, pc.logger)
- if pc.cfg.Global.AuthEnabled == true {
- // work around before metadata is available
- file, err := os.Open("/etc/kubernetes/pc_login_info")
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Authentication is enabled but found no username/password at /etc/kubernetes/pc_login_info. Error[%v]", err)
- return nil, err
- }
- defer file.Close()
- scanner := bufio.NewScanner(file)
- if !scanner.Scan() {
- klog.Error("Photon Cloud Provider: Empty username inside /etc/kubernetes/pc_login_info.")
- return nil, fmt.Errorf("Failed to create authentication enabled client with invalid username")
- }
- username := scanner.Text()
- if !scanner.Scan() {
- klog.Error("Photon Cloud Provider: Empty password set inside /etc/kubernetes/pc_login_info.")
- return nil, fmt.Errorf("Failed to create authentication enabled client with invalid password")
- }
- password := scanner.Text()
- tokenOptions, err := pc.photonClient.Auth.GetTokensByPassword(username, password)
- if err != nil {
- klog.Error("Photon Cloud Provider: failed to get tokens by password")
- return nil, err
- }
- options = &photon.ClientOptions{
- IgnoreCertificate: true,
- TokenOptions: &photon.TokenOptions{
- AccessToken: tokenOptions.AccessToken,
- },
- }
- pc.photonClient = photon.NewClient(pc.cfg.Global.CloudTarget, options, pc.logger)
- }
- status, err := pc.photonClient.Status.Get()
- if err != nil {
- klog.Errorf("Photon Cloud Provider: new client creation failed. Error[%v]", err)
- return nil, err
- }
- klog.V(2).Infof("Photon Cloud Provider: Status of the new photon controller client: %v", status)
- return pc.photonClient, nil
- }
- func newPCCloud(cfg PCConfig) (*PCCloud, error) {
- projID := cfg.Global.Project
- vmID := cfg.Global.VMID
- // Get local hostname
- hostname, err := os.Hostname()
- if err != nil {
- klog.Errorf("Photon Cloud Provider: get hostname failed. Error[%v]", err)
- return nil, err
- }
- pc := PCCloud{
- cfg: &cfg,
- localInstanceID: vmID,
- localHostname: hostname,
- localK8sHostname: "",
- projID: projID,
- }
- overrideIP = cfg.Global.OverrideIP
- return &pc, nil
- }
- // Initialize passes a Kubernetes clientBuilder interface to the cloud provider
- func (pc *PCCloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) {
- }
- // Instances returns an implementation of Instances for Photon Controller.
- func (pc *PCCloud) Instances() (cloudprovider.Instances, bool) {
- return pc, true
- }
- // List is an implementation of Instances.List.
- func (pc *PCCloud) List(filter string) ([]k8stypes.NodeName, error) {
- return nil, nil
- }
- // NodeAddresses is an implementation of Instances.NodeAddresses.
- func (pc *PCCloud) NodeAddresses(ctx context.Context, nodeName k8stypes.NodeName) ([]v1.NodeAddress, error) {
- nodeAddrs := []v1.NodeAddress{}
- name := string(nodeName)
- if name == pc.localK8sHostname {
- ifaces, err := net.Interfaces()
- if err != nil {
- klog.Errorf("Photon Cloud Provider: net.Interfaces() failed for NodeAddresses. Error[%v]", err)
- return nodeAddrs, err
- }
- for _, i := range ifaces {
- addrs, err := i.Addrs()
- if err != nil {
- klog.Warningf("Photon Cloud Provider: Failed to extract addresses for NodeAddresses. Error[%v]", err)
- } else {
- for _, addr := range addrs {
- if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
- if ipnet.IP.To4() != nil {
- // Filter external IP by MAC address OUIs from vCenter and from ESX
- if strings.HasPrefix(i.HardwareAddr.String(), MAC_OUI_VC) ||
- strings.HasPrefix(i.HardwareAddr.String(), MAC_OUI_ESX) {
- nodehelpers.AddToNodeAddresses(&nodeAddrs,
- v1.NodeAddress{
- Type: v1.NodeExternalIP,
- Address: ipnet.IP.String(),
- },
- )
- } else {
- nodehelpers.AddToNodeAddresses(&nodeAddrs,
- v1.NodeAddress{
- Type: v1.NodeInternalIP,
- Address: ipnet.IP.String(),
- },
- )
- }
- }
- }
- }
- }
- }
- return nodeAddrs, nil
- }
- // Inquiring IP addresses from photon controller endpoint only for a node other than this node.
- // This is assumed to be done by master only.
- vmID, err := getInstanceID(pc, name)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: getInstanceID failed for NodeAddresses. Error[%v]", err)
- return nodeAddrs, err
- }
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for NodeAddresses, error: [%v]", err)
- return nodeAddrs, err
- }
- // Retrieve the Photon VM's IP addresses from the Photon Controller endpoint based on the VM ID
- vmList, err := photonClient.Projects.GetVMs(pc.projID, nil)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to GetVMs for project %s. Error[%v]", pc.projID, err)
- return nodeAddrs, err
- }
- for _, vm := range vmList.Items {
- if vm.ID == vmID {
- task, err := photonClient.VMs.GetNetworks(vm.ID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: GetNetworks failed for node %s with vm.ID %s. Error[%v]", name, vm.ID, err)
- return nodeAddrs, err
- } else {
- task, err = photonClient.Tasks.Wait(task.ID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Wait task for GetNetworks failed for node %s with vm.ID %s. Error[%v]", name, vm.ID, err)
- return nodeAddrs, err
- } else {
- networkConnections := task.ResourceProperties.(map[string]interface{})
- networks := networkConnections["networkConnections"].([]interface{})
- for _, nt := range networks {
- ipAddr := "-"
- macAddr := "-"
- network := nt.(map[string]interface{})
- if val, ok := network["ipAddress"]; ok && val != nil {
- ipAddr = val.(string)
- }
- if val, ok := network["macAddress"]; ok && val != nil {
- macAddr = val.(string)
- }
- if ipAddr != "-" {
- if strings.HasPrefix(macAddr, MAC_OUI_VC) ||
- strings.HasPrefix(macAddr, MAC_OUI_ESX) {
- nodehelpers.AddToNodeAddresses(&nodeAddrs,
- v1.NodeAddress{
- Type: v1.NodeExternalIP,
- Address: ipAddr,
- },
- )
- } else {
- nodehelpers.AddToNodeAddresses(&nodeAddrs,
- v1.NodeAddress{
- Type: v1.NodeInternalIP,
- Address: ipAddr,
- },
- )
- }
- }
- }
- return nodeAddrs, nil
- }
- }
- }
- }
- klog.Errorf("Failed to find the node %s from Photon Controller endpoint", name)
- return nodeAddrs, fmt.Errorf("Failed to find the node %s from Photon Controller endpoint", name)
- }
- // NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
- // This method will not be called from the node that is requesting this ID. i.e. metadata service
- // and other local methods cannot be used here
- func (pc *PCCloud) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) {
- return []v1.NodeAddress{}, cloudprovider.NotImplemented
- }
- func (pc *PCCloud) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error {
- return cloudprovider.NotImplemented
- }
- func (pc *PCCloud) CurrentNodeName(ctx context.Context, hostname string) (k8stypes.NodeName, error) {
- pc.localK8sHostname = hostname
- return k8stypes.NodeName(hostname), nil
- }
- func getInstanceID(pc *PCCloud, name string) (string, error) {
- var vmID string
- var err error
- if overrideIP == true {
- vmID, err = getVMIDbyIP(pc, name)
- } else {
- vmID, err = getVMIDbyNodename(pc, name)
- }
- if err != nil {
- return "", err
- }
- if vmID == "" {
- err = cloudprovider.InstanceNotFound
- }
- return vmID, err
- }
- // InstanceExistsByProviderID returns true if the instance with the given provider id still exists and is running.
- // If false is returned with no error, the instance will be immediately deleted by the cloud controller manager.
- func (pc *PCCloud) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) {
- return false, cloudprovider.NotImplemented
- }
- // InstanceShutdownByProviderID returns true if the instance is in safe state to detach volumes
- func (pc *PCCloud) InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error) {
- return false, cloudprovider.NotImplemented
- }
- // InstanceID returns the cloud provider ID of the specified instance.
- func (pc *PCCloud) InstanceID(ctx context.Context, nodeName k8stypes.NodeName) (string, error) {
- name := string(nodeName)
- if name == pc.localK8sHostname {
- return pc.localInstanceID, nil
- }
- // We assume only master need to get InstanceID of a node other than itself
- id, err := getInstanceID(pc, name)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: getInstanceID failed for InstanceID. Error[%v]", err)
- }
- return id, err
- }
- // InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
- // This method will not be called from the node that is requesting this ID. i.e. metadata service
- // and other local methods cannot be used here
- func (pc *PCCloud) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) {
- return "", cloudprovider.NotImplemented
- }
- func (pc *PCCloud) InstanceType(ctx context.Context, nodeName k8stypes.NodeName) (string, error) {
- return "", nil
- }
- func (pc *PCCloud) Clusters() (cloudprovider.Clusters, bool) {
- return nil, true
- }
- // ProviderName returns the cloud provider ID.
- func (pc *PCCloud) ProviderName() string {
- return ProviderName
- }
- // LoadBalancer returns an implementation of LoadBalancer for Photon Controller.
- func (pc *PCCloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
- return nil, false
- }
- // Zones returns an implementation of Zones for Photon Controller.
- func (pc *PCCloud) Zones() (cloudprovider.Zones, bool) {
- return pc, true
- }
- func (pc *PCCloud) GetZone(ctx context.Context) (cloudprovider.Zone, error) {
- return pc.Zone, nil
- }
- // GetZoneByProviderID implements Zones.GetZoneByProviderID
- // This is particularly useful in external cloud providers where the kubelet
- // does not initialize node data.
- func (pc *PCCloud) GetZoneByProviderID(ctx context.Context, providerID string) (cloudprovider.Zone, error) {
- return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented")
- }
- // GetZoneByNodeName implements Zones.GetZoneByNodeName
- // This is particularly useful in external cloud providers where the kubelet
- // does not initialize node data.
- func (pc *PCCloud) GetZoneByNodeName(ctx context.Context, nodeName k8stypes.NodeName) (cloudprovider.Zone, error) {
- return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented")
- }
- // Routes returns a false since the interface is not supported for photon controller.
- func (pc *PCCloud) Routes() (cloudprovider.Routes, bool) {
- return nil, false
- }
- // HasClusterID returns true if the cluster has a clusterID
- func (pc *PCCloud) HasClusterID() bool {
- return true
- }
- // AttachDisk attaches given virtual disk volume to the compute running kubelet.
- func (pc *PCCloud) AttachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error {
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for AttachDisk, error: [%v]", err)
- return err
- }
- operation := &photon.VmDiskOperation{
- DiskID: pdID,
- }
- vmID, err := pc.InstanceID(ctx, nodeName)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for AttachDisk. Error[%v]", err)
- return err
- }
- task, err := photonClient.VMs.AttachDisk(vmID, operation)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to attach disk with pdID %s. Error[%v]", pdID, err)
- return err
- }
- _, err = photonClient.Tasks.Wait(task.ID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to wait for task to attach disk with pdID %s. Error[%v]", pdID, err)
- return err
- }
- return nil
- }
- // Detaches given virtual disk volume from the compute running kubelet.
- func (pc *PCCloud) DetachDisk(ctx context.Context, pdID string, nodeName k8stypes.NodeName) error {
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for DetachDisk, error: [%v]", err)
- return err
- }
- operation := &photon.VmDiskOperation{
- DiskID: pdID,
- }
- vmID, err := pc.InstanceID(ctx, nodeName)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for DetachDisk. Error[%v]", err)
- return err
- }
- task, err := photonClient.VMs.DetachDisk(vmID, operation)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to detach disk with pdID %s. Error[%v]", pdID, err)
- return err
- }
- _, err = photonClient.Tasks.Wait(task.ID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to wait for task to detach disk with pdID %s. Error[%v]", pdID, err)
- return err
- }
- return nil
- }
- // DiskIsAttached returns if disk is attached to the VM using controllers supported by the plugin.
- func (pc *PCCloud) DiskIsAttached(ctx context.Context, pdID string, nodeName k8stypes.NodeName) (bool, error) {
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for DiskIsAttached, error: [%v]", err)
- return false, err
- }
- disk, err := photonClient.Disks.Get(pdID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to Get disk with pdID %s. Error[%v]", pdID, err)
- return false, err
- }
- vmID, err := pc.InstanceID(ctx, nodeName)
- if err == cloudprovider.InstanceNotFound {
- klog.Infof("Instance %q does not exist, disk %s will be detached automatically.", nodeName, pdID)
- return false, nil
- }
- if err != nil {
- klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for DiskIsAttached. Error[%v]", err)
- return false, err
- }
- for _, vm := range disk.VMs {
- if vm == vmID {
- return true, nil
- }
- }
- return false, nil
- }
- // DisksAreAttached returns if disks are attached to the VM using controllers supported by the plugin.
- func (pc *PCCloud) DisksAreAttached(ctx context.Context, pdIDs []string, nodeName k8stypes.NodeName) (map[string]bool, error) {
- attached := make(map[string]bool)
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for DisksAreAttached, error: [%v]", err)
- return attached, err
- }
- for _, pdID := range pdIDs {
- attached[pdID] = false
- }
- vmID, err := pc.InstanceID(ctx, nodeName)
- if err == cloudprovider.InstanceNotFound {
- klog.Infof("Instance %q does not exist, its disks will be detached automatically.", nodeName)
- // make all the disks as detached.
- return attached, nil
- }
- if err != nil {
- klog.Errorf("Photon Cloud Provider: pc.InstanceID failed for DiskIsAttached. Error[%v]", err)
- return attached, err
- }
- for _, pdID := range pdIDs {
- disk, err := photonClient.Disks.Get(pdID)
- if err != nil {
- klog.Warningf("Photon Cloud Provider: failed to get VMs for persistent disk %s, err [%v]", pdID, err)
- } else {
- for _, vm := range disk.VMs {
- if vm == vmID {
- attached[pdID] = true
- }
- }
- }
- }
- return attached, nil
- }
- // Create a volume of given size (in GB).
- func (pc *PCCloud) CreateDisk(volumeOptions *VolumeOptions) (pdID string, err error) {
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for CreateDisk, error: [%v]", err)
- return "", err
- }
- diskSpec := photon.DiskCreateSpec{}
- diskSpec.Name = volumeOptions.Name
- diskSpec.Flavor = volumeOptions.Flavor
- diskSpec.CapacityGB = volumeOptions.CapacityGB
- diskSpec.Kind = DiskSpecKind
- task, err := photonClient.Projects.CreateDisk(pc.projID, &diskSpec)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to CreateDisk. Error[%v]", err)
- return "", err
- }
- waitTask, err := photonClient.Tasks.Wait(task.ID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to wait for task to CreateDisk. Error[%v]", err)
- return "", err
- }
- return waitTask.Entity.ID, nil
- }
- // DeleteDisk deletes a volume given volume name.
- func (pc *PCCloud) DeleteDisk(pdID string) error {
- photonClient, err := getPhotonClient(pc)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to get photon client for DeleteDisk, error: [%v]", err)
- return err
- }
- task, err := photonClient.Disks.Delete(pdID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to DeleteDisk. Error[%v]", err)
- return err
- }
- _, err = photonClient.Tasks.Wait(task.ID)
- if err != nil {
- klog.Errorf("Photon Cloud Provider: Failed to wait for task to DeleteDisk. Error[%v]", err)
- return err
- }
- return nil
- }
|