// nextSubnetIPAddress returns the next available IP address in the given subnet. func nextSubnetIPAddress( nicClient network.InterfacesClient, resourceGroup string, subnet *network.Subnet, ) (string, error) { _, ipnet, err := net.ParseCIDR(to.String(subnet.Properties.AddressPrefix)) if err != nil { return "", errors.Annotate(err, "parsing subnet prefix") } results, err := nicClient.List(resourceGroup) if err != nil { return "", errors.Annotate(err, "listing NICs") } // Azure reserves the first 4 addresses in the subnet. var ipsInUse []net.IP if results.Value != nil { ipsInUse = make([]net.IP, 0, len(*results.Value)) for _, item := range *results.Value { if item.Properties.IPConfigurations == nil { continue } for _, ipConfiguration := range *item.Properties.IPConfigurations { if to.String(ipConfiguration.Properties.Subnet.ID) != to.String(subnet.ID) { continue } ip := net.ParseIP(to.String(ipConfiguration.Properties.PrivateIPAddress)) if ip != nil { ipsInUse = append(ipsInUse, ip) } } } } ip, err := iputils.NextSubnetIP(ipnet, ipsInUse) if err != nil { return "", errors.Trace(err) } return ip.String(), nil }
// instanceNetworkInterfaces lists all network interfaces in the resource // group, and returns a mapping from instance ID to the network interfaces // associated with that instance. func instanceNetworkInterfaces( callAPI callAPIFunc, resourceGroup string, nicClient network.InterfacesClient, ) (map[instance.Id][]network.Interface, error) { var nicsResult network.InterfaceListResult if err := callAPI(func() (autorest.Response, error) { var err error nicsResult, err = nicClient.List(resourceGroup) return nicsResult.Response, err }); err != nil { return nil, errors.Annotate(err, "listing network interfaces") } if nicsResult.Value == nil || len(*nicsResult.Value) == 0 { return nil, nil } instanceNics := make(map[instance.Id][]network.Interface) for _, nic := range *nicsResult.Value { instanceId := instance.Id(toTags(nic.Tags)[jujuMachineNameTag]) instanceNics[instanceId] = append(instanceNics[instanceId], nic) } return instanceNics, nil }
// allInstances returns all of the instances in the given resource group, // and optionally ensures that each instance's addresses are up-to-date. func (env *azureEnviron) allInstances( resourceGroup string, refreshAddresses bool, ) ([]instance.Instance, error) { env.mu.Lock() vmClient := compute.VirtualMachinesClient{env.compute} nicClient := network.InterfacesClient{env.network} pipClient := network.PublicIPAddressesClient{env.network} env.mu.Unlock() // Due to how deleting instances works, we have to get creative about // listing instances. We list NICs and return an instance for each // unique value of the jujuMachineNameTag tag. // // The machine provisioner will call AllInstances so it can delete // unknown instances. StopInstances must delete VMs before NICs and // public IPs, because a VM cannot have less than 1 NIC. Thus, we can // potentially delete a VM but then fail to delete its NIC. nicsResult, err := nicClient.List(resourceGroup) if err != nil { if nicsResult.Response.Response != nil && nicsResult.StatusCode == http.StatusNotFound { // This will occur if the resource group does not // exist, e.g. in a fresh hosted environment. return nil, nil } return nil, errors.Trace(err) } if nicsResult.Value == nil || len(*nicsResult.Value) == 0 { return nil, nil } // Create an azureInstance for each VM. result, err := vmClient.List(resourceGroup) if err != nil { return nil, errors.Annotate(err, "listing virtual machines") } vmNames := make(set.Strings) var azureInstances []*azureInstance if result.Value != nil { azureInstances = make([]*azureInstance, len(*result.Value)) for i, vm := range *result.Value { inst := &azureInstance{vm, env, nil, nil} azureInstances[i] = inst vmNames.Add(to.String(vm.Name)) } } // Create additional azureInstances for NICs without machines. See // comments above for rationale. This needs to happen before calling // setInstanceAddresses, so we still associate the NICs/PIPs. for _, nic := range *nicsResult.Value { vmName, ok := toTags(nic.Tags)[jujuMachineNameTag] if !ok || vmNames.Contains(vmName) { continue } vm := compute.VirtualMachine{ Name: to.StringPtr(vmName), Properties: &compute.VirtualMachineProperties{ ProvisioningState: to.StringPtr("Partially Deleted"), }, } inst := &azureInstance{vm, env, nil, nil} azureInstances = append(azureInstances, inst) vmNames.Add(to.String(vm.Name)) } if len(azureInstances) > 0 && refreshAddresses { if err := setInstanceAddresses( pipClient, resourceGroup, azureInstances, nicsResult, ); err != nil { return nil, errors.Trace(err) } } instances := make([]instance.Instance, len(azureInstances)) for i, inst := range azureInstances { instances[i] = inst } return instances, nil }