~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/provider/azure/instance.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
        "net/http"
9
9
        "strings"
10
10
 
11
 
        "github.com/Azure/azure-sdk-for-go/arm/compute"
12
11
        "github.com/Azure/azure-sdk-for-go/arm/network"
13
12
        "github.com/Azure/go-autorest/autorest"
14
13
        "github.com/Azure/go-autorest/autorest/to"
21
20
)
22
21
 
23
22
type azureInstance struct {
24
 
        compute.VirtualMachine
 
23
        vmName            string
 
24
        provisioningState string
25
25
        env               *azureEnviron
26
26
        networkInterfaces []network.Interface
27
27
        publicIPAddresses []network.PublicIPAddress
32
32
        // Note: we use Name and not Id, since all VM operations are in
33
33
        // terms of the VM name (qualified by resource group). The ID is
34
34
        // an internal detail.
35
 
        return instance.Id(to.String(inst.VirtualMachine.Name))
 
35
        return instance.Id(inst.vmName)
36
36
}
37
37
 
38
38
// Status is specified in the Instance interface.
39
39
func (inst *azureInstance) Status() instance.InstanceStatus {
40
 
        // NOTE(axw) ideally we would use the power state, but that is only
41
 
        // available when using the "instance view". Instance view is only
42
 
        // delivered when explicitly requested, and you can only request it
43
 
        // when querying a single VM. This means the results of AllInstances
44
 
        // or Instances would have the instance view missing.
45
 
        //
46
 
        // TODO(axw) if the provisioning state is "Failed", then
47
 
        // we should query the operation status and report the error
48
 
        // here.
 
40
        instanceStatus := status.Empty
 
41
        message := inst.provisioningState
 
42
        switch inst.provisioningState {
 
43
        case "Succeeded":
 
44
                // TODO(axw) once a VM has been started, we should
 
45
                // start using its power state to show if it's
 
46
                // really running or not. This is just a nice to
 
47
                // have, since we should not expect a VM to ever
 
48
                // be stopped.
 
49
                instanceStatus = status.Running
 
50
                message = ""
 
51
        case "Canceled", "Failed":
 
52
                // TODO(axw) if the provisioning state is "Failed", then we
 
53
                // should use the error message from the deployment description
 
54
                // as the Message. The error details are not currently exposed
 
55
                // in the Azure SDK. See:
 
56
                //     https://github.com/Azure/azure-sdk-for-go/issues/399
 
57
                instanceStatus = status.ProvisioningError
 
58
        case "Running":
 
59
                message = ""
 
60
                fallthrough
 
61
        default:
 
62
                instanceStatus = status.Provisioning
 
63
        }
49
64
        return instance.InstanceStatus{
50
 
                Status:  status.StatusEmpty,
51
 
                Message: to.String(inst.Properties.ProvisioningState),
 
65
                Status:  instanceStatus,
 
66
                Message: message,
52
67
        }
53
 
 
54
68
}
55
69
 
56
70
// setInstanceAddresses queries Azure for the NICs and public IPs associated
58
72
// VirtualMachines are up-to-date, and that there are no concurrent accesses
59
73
// to the instances.
60
74
func setInstanceAddresses(
 
75
        callAPI callAPIFunc,
 
76
        resourceGroup string,
 
77
        nicClient network.InterfacesClient,
61
78
        pipClient network.PublicIPAddressesClient,
62
 
        resourceGroup string,
63
79
        instances []*azureInstance,
64
 
        nicsResult network.InterfaceListResult,
65
80
) (err error) {
66
 
 
67
 
        instanceNics := make(map[instance.Id][]network.Interface)
68
 
        instancePips := make(map[instance.Id][]network.PublicIPAddress)
 
81
        instanceNics, err := instanceNetworkInterfaces(
 
82
                callAPI, resourceGroup, nicClient,
 
83
        )
 
84
        if err != nil {
 
85
                return errors.Annotate(err, "listing network interfaces")
 
86
        }
 
87
        instancePips, err := instancePublicIPAddresses(
 
88
                callAPI, resourceGroup, pipClient,
 
89
        )
 
90
        if err != nil {
 
91
                return errors.Annotate(err, "listing public IP addresses")
 
92
        }
69
93
        for _, inst := range instances {
70
 
                instanceNics[inst.Id()] = nil
71
 
                instancePips[inst.Id()] = nil
72
 
        }
73
 
 
74
 
        // When setAddresses returns without error, update each
75
 
        // instance's network interfaces and public IP addresses.
76
 
        setInstanceFields := func(inst *azureInstance) {
77
94
                inst.networkInterfaces = instanceNics[inst.Id()]
78
95
                inst.publicIPAddresses = instancePips[inst.Id()]
79
96
        }
80
 
        defer func() {
81
 
                if err != nil {
82
 
                        return
83
 
                }
84
 
                for _, inst := range instances {
85
 
                        setInstanceFields(inst)
86
 
                }
87
 
        }()
88
 
 
89
 
        // We do not rely on references because of how StopInstances works.
90
 
        // In order to not leak resources we must not delete the virtual
91
 
        // machine until after all of its dependencies are deleted.
92
 
        //
93
 
        // NICs and PIPs cannot be deleted until they have no references.
94
 
        // Thus, we cannot delete a PIP until there is no reference to it
95
 
        // in any NICs, and likewise we cannot delete a NIC until there
96
 
        // is no reference to it in any virtual machine.
97
 
 
98
 
        if nicsResult.Value != nil {
99
 
                for _, nic := range *nicsResult.Value {
100
 
                        instanceId := instance.Id(toTags(nic.Tags)[jujuMachineNameTag])
101
 
                        if _, ok := instanceNics[instanceId]; !ok {
102
 
                                continue
103
 
                        }
104
 
                        instanceNics[instanceId] = append(instanceNics[instanceId], nic)
105
 
                }
106
 
        }
107
 
 
108
 
        pipsResult, err := pipClient.List(resourceGroup)
109
 
        if err != nil {
110
 
                return errors.Annotate(err, "listing public IP addresses")
111
 
        }
112
 
        if pipsResult.Value != nil {
113
 
                for _, pip := range *pipsResult.Value {
114
 
                        instanceId := instance.Id(toTags(pip.Tags)[jujuMachineNameTag])
115
 
                        if _, ok := instanceNics[instanceId]; !ok {
116
 
                                continue
117
 
                        }
118
 
                        instancePips[instanceId] = append(instancePips[instanceId], pip)
119
 
                }
120
 
        }
121
 
 
122
 
        // Fields will be assigned to instances by the deferred call.
123
97
        return nil
124
98
}
125
99
 
 
100
// instanceNetworkInterfaces lists all network interfaces in the resource
 
101
// group, and returns a mapping from instance ID to the network interfaces
 
102
// associated with that instance.
 
103
func instanceNetworkInterfaces(
 
104
        callAPI callAPIFunc,
 
105
        resourceGroup string,
 
106
        nicClient network.InterfacesClient,
 
107
) (map[instance.Id][]network.Interface, error) {
 
108
        var nicsResult network.InterfaceListResult
 
109
        if err := callAPI(func() (autorest.Response, error) {
 
110
                var err error
 
111
                nicsResult, err = nicClient.List(resourceGroup)
 
112
                return nicsResult.Response, err
 
113
        }); err != nil {
 
114
                return nil, errors.Annotate(err, "listing network interfaces")
 
115
        }
 
116
        if nicsResult.Value == nil || len(*nicsResult.Value) == 0 {
 
117
                return nil, nil
 
118
        }
 
119
        instanceNics := make(map[instance.Id][]network.Interface)
 
120
        for _, nic := range *nicsResult.Value {
 
121
                instanceId := instance.Id(toTags(nic.Tags)[jujuMachineNameTag])
 
122
                instanceNics[instanceId] = append(instanceNics[instanceId], nic)
 
123
        }
 
124
        return instanceNics, nil
 
125
}
 
126
 
 
127
// interfacePublicIPAddresses lists all public IP addresses in the resource
 
128
// group, and returns a mapping from instance ID to the public IP addresses
 
129
// associated with that instance.
 
130
func instancePublicIPAddresses(
 
131
        callAPI callAPIFunc,
 
132
        resourceGroup string,
 
133
        pipClient network.PublicIPAddressesClient,
 
134
) (map[instance.Id][]network.PublicIPAddress, error) {
 
135
        var pipsResult network.PublicIPAddressListResult
 
136
        if err := callAPI(func() (autorest.Response, error) {
 
137
                var err error
 
138
                pipsResult, err = pipClient.List(resourceGroup)
 
139
                return pipsResult.Response, err
 
140
        }); err != nil {
 
141
                return nil, errors.Annotate(err, "listing public IP addresses")
 
142
        }
 
143
        if pipsResult.Value == nil || len(*pipsResult.Value) == 0 {
 
144
                return nil, nil
 
145
        }
 
146
        instancePips := make(map[instance.Id][]network.PublicIPAddress)
 
147
        for _, pip := range *pipsResult.Value {
 
148
                instanceId := instance.Id(toTags(pip.Tags)[jujuMachineNameTag])
 
149
                instancePips[instanceId] = append(instancePips[instanceId], pip)
 
150
        }
 
151
        return instancePips, nil
 
152
}
 
153
 
126
154
// Addresses is specified in the Instance interface.
127
155
func (inst *azureInstance) Addresses() ([]jujunetwork.Address, error) {
128
156
        addresses := make([]jujunetwork.Address, 0, len(inst.networkInterfaces)+len(inst.publicIPAddresses))