Skip to content

Commit

Permalink
Merge pull request juju#10979 from achilleasa/dev-fetch-network-inter…
Browse files Browse the repository at this point in the history
…faces-in-instance-poller

juju#10979

## Description of change

This PR concludes the refactoring work for the instance poller.

### The old approach

Prior to this PR, the machiner worker would periodically wake up, collect the device information that was visible to it and sent it to the controller via the `SetObservedNetworkConfig` API call. The implementation for this API method (lives in the `networkingcommon` package) would then query the network provider for additional interface info and merge that to the data sent in by the machiner.

The final piece of fused data would then be broken down into two slices:
- a `[]state.LinkLayerDeviceArgs`
- a `[]state.LinkLayerDeviceAddresses`

A state-based setter method would then be invoked for each one of those slices to update the link layer devices collection and the machine addresses collection. However, the original approach had some caveats:

- We would need to query the provider for network info each time that the machiner worker wakes up; if we have deployed a large number of machines we would be making an API call to the provider for each machine in the model even though the provider-based information doesn't really change that often.
- As far as detecting, populating (and keeping in sync) the **provider** addresses for the machine we were currently relying on the fact that the machiner worker is running and always able to connect to the API server.

### The new approach

In the new world, the machiner worker is the primary source for collecting and updating the link layer device (and LLD address) collection. The code that queried the underlying provider for extra info has now been removed from the `SetObservedNetworkConfig` code path.

Instead, the instancepoller (a more dependable worker since it runs on the controller) will now also collect (if the provider supports it) the interface information using a single batch call at the same time when it queries the instance information (yet another batch call). If the provider does not support querying for network interfaces (e.g. MAAS, LXD etc.), the instancepoller will create a fake list of minimal network interface information using the address info obtained via the instance status query.

This information is then sent to the controller via a new instancepoller API method called `SetProviderNetworkConfig`. The controller-side implementation:

- attempts to infer the space ID for each provided interface address (private and/or shadow) using one of the following mechanisms (depending on what bits of information is provided by the instance poller):
 - match address to a subnet CIDR (multiple matches are considered ambiguous and will cause an error)
 - match CIDR **and** providerID to a subnet (this one is un-ambiguous)
 
In the case where we cannot match an address to any subnet (e.g. public shadow address), we automatically assign it to the **alpha** space. The operator can (will be able to) later use the juju CLI and re-assign the address so using a sane default should not cause any issues.

Finally, since the interface information pushed by the instance poller may contain provider specific IDs (network, address, subnet IDs) that are not visible to the machiner worker, the introduced `SetProviderNetworkConfig` also makes a _best-effort_ attempt to match (by address) each instancepoller-reported interface to an existing linklayer device/address combination and backfill the provider specific bits.

## Caveats and potential issues

Since the instancepoller will detect the machine before it actually starts (hence, before the machiner worker runs) it would be a good opportunity to pre-populate the link layer device/address collections from the instancepoller-sourced data. 

Unfortunately, some providers (I am looking at you ec2!) do not report the names for network interfaces **and** our linklayer device document IDs include the interface name. Therefore, pre-populating and then merging the machiner worker information is not really an option as we will end up with different documents.

The chosen approach does not have this problem as we only backfill the provider ID bits to devices that have been already added by the machiner. The caveat here is that this backfill process will take a while to complete; once a machine enters the long poll group, it won't be refreshed for about 15 minutes which is more or less the latency we expect to see.

Furthermore, I would like to point out that while testing this code, I have noticed that in the case of EC2, the link layer device collection and its sibling IP address collection **DOES** in fact include duplicate entries for the same link layer device. This also happens in the 2.6 branch which does not include any of the instancepoller refactoring work and might allude to a potential bug.

The following examples (link layer device and ip.address collection bits) were obtained by bootstrapping a 2.6 controller on AWS

```json
{
 "_id" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0:m#0#d#unsupported0",
 "name" : "unsupported0",
 "model-uuid" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0",
 "mtu" : 0,
 "providerid" : "eni-067314af609749f26",
 "machine-id" : "0",
 "type" : "ethernet",
 "mac-address" : "0e:89:85:5c:80:47",
 "is-auto-start" : true,
 "is-up" : true,
 "parent-name" : "",
 "txn-revno" : NumberLong(2),
 "txn-queue" : [
 "5de64e7d90f35328503a5868_33348be2",
 "5de64e7d90f35328503a5869_d8534326"
 ]
}
...
{
 "_id" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0:m#0#d#ens5",
 "name" : "ens5",
 "model-uuid" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0",
 "mtu" : 9001,
 "machine-id" : "0",
 "type" : "ethernet",
 "mac-address" : "0e:89:85:5c:80:47",
 "is-auto-start" : true,
 "is-up" : true,
 "parent-name" : "",
 "txn-revno" : NumberLong(2),
 "txn-queue" : [
 "5de64e7d90f35328503a586d_0056020f",
 "5de64e7d90f35328503a586f_b036dc72"
 ]
}
```

```json
{
 "_id" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0:m#0#d#unsupported0#ip#172.31.36.2",
 "model-uuid" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0",
 "device-name" : "unsupported0",
 "machine-id" : "0",
 "subnet-cidr" : "172.31.32.0/20",
 "config-method" : "dynamic",
 "value" : "172.31.36.2",
 "txn-revno" : NumberLong(2),
 "txn-queue" : [
 "5de64e7d90f35328503a5869_d8534326"
 ]
}
...
{
 "_id" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0:m#0#d#ens5#ip#172.31.36.2",
 "model-uuid" : "181280d3-d776-4790-82bc-fd7f7b7bd8a0",
 "device-name" : "ens5",
 "machine-id" : "0",
 "subnet-cidr" : "172.31.32.0/20",
 "config-method" : "static",
 "value" : "172.31.36.2",
 "gateway-address" : "172.31.32.1",
 "is-default-gateway" : true,
 "txn-revno" : NumberLong(2),
 "txn-queue" : [
 "5de64e7d90f35328503a586f_b036dc72"
 ]
}
```

## QA steps

Bootstrap on ec2, gce, maas and lxd with 
```--logging-config='<root>=ERROR;juju.worker.instancepoller=DEBUG;juju.apiserver.instancepoller=DEBUG'```

If the provider supports spaces, create a new space from a subnet and spin up a machine on both the alpha (or the default equivalent on MAAS) and the new space. Tailing the debug logs should report that the instancepoller has detected new provider addresses which should include a space ID as their suffix.

```console
$ juju debug-log -m {controller/default} --include-module juju.worker.instancepoller --include-module juju.apiserver.instancepoller --replay --level DEBUG
```
  • Loading branch information
jujubot authored Dec 5, 2019
2 parents 432d265 + dcd69a1 commit 2791d2f
Show file tree
Hide file tree
Showing 33 changed files with 1,706 additions and 299 deletions.
2 changes: 1 addition & 1 deletion api/facadeversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ var facadeVersions = map[string]int{
"ImageMetadata": 3,
"ImageMetadataManager": 1,
"InstanceMutater": 2,
"InstancePoller": 3,
"InstancePoller": 4,
"KeyManager": 1,
"KeyUpdater": 1,
"LeadershipService": 2,
Expand Down
43 changes: 16 additions & 27 deletions api/instancepoller/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,40 +152,29 @@ func (m *Machine) SetInstanceStatus(status status.Status, message string, data m
return result.OneError()
}

// ProviderAddresses returns all addresses of the machine
// known to the cloud provider.
func (m *Machine) ProviderAddresses() (network.ProviderAddresses, error) {
var results params.MachineAddressesResults
args := params.Entities{Entities: []params.Entity{
{Tag: m.tag.String()},
}}
err := m.facade.FacadeCall("ProviderAddresses", args, &results)
// SetProviderNetworkConfig updates the provider addresses for this machine.
func (m *Machine) SetProviderNetworkConfig(ifList []network.InterfaceInfo) (network.ProviderAddresses, bool, error) {
var results params.SetProviderNetworkConfigResults
args := params.SetProviderNetworkConfig{
Args: []params.ProviderNetworkConfig{{
Tag: m.tag.String(),
Configs: params.NetworkConfigFromInterfaceInfo(ifList),
}},
}

err := m.facade.FacadeCall("SetProviderNetworkConfig", args, &results)
if err != nil {
return nil, errors.Trace(err)
return nil, false, err
}

if len(results.Results) != 1 {
err := errors.Errorf("expected 1 result, got %d", len(results.Results))
return nil, err
return nil, false, err
}
result := results.Results[0]
if result.Error != nil {
return nil, result.Error
return nil, false, result.Error
}
return params.ToProviderAddresses(result.Addresses...), nil
}

// SetProviderAddresses sets the cached provider
// addresses for the machine.
func (m *Machine) SetProviderAddresses(addrs ...network.ProviderAddress) error {
var result params.ErrorResults
args := params.SetMachinesAddresses{
MachineAddresses: []params.MachineAddresses{{
Tag: m.tag.String(),
Addresses: params.FromProviderAddresses(addrs...),
}}}
err := m.facade.FacadeCall("SetProviderAddresses", args, &result)
if err != nil {
return err
}
return result.OneError()
return params.ToProviderAddresses(result.Addresses...), result.Modified, nil
}
55 changes: 21 additions & 34 deletions api/instancepoller/machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,12 @@ var machineErrorTests = []struct {
},
resultsRef: params.ErrorResults{},
}, {
method: "ToProviderAddresses",
method: "SetProviderNetworkConfig",
wrapper: func(m *instancepoller.Machine) error {
_, err := m.ProviderAddresses()
_, _, err := m.SetProviderNetworkConfig(nil)
return err
},
resultsRef: params.MachineAddressesResults{},
}, {
method: "SetProviderAddresses",
wrapper: func(m *instancepoller.Machine) error {
return m.SetProviderAddresses()
},
resultsRef: params.ErrorResults{},
resultsRef: params.SetProviderNetworkConfigResults{},
}}

func (s *MachineSuite) TestClientError(c *gc.C) {
Expand Down Expand Up @@ -231,33 +225,26 @@ func (s *MachineSuite) TestSetInstanceStatusSuccess(c *gc.C) {
c.Check(apiCaller.CallCount, gc.Equals, 1)
}

func (s *MachineSuite) TestProviderAddressesSuccess(c *gc.C) {
addresses := network.NewProviderAddresses("2001:db8::1", "0.1.2.3")
results := params.MachineAddressesResults{
Results: []params.MachineAddressesResult{{
Addresses: params.FromProviderAddresses(addresses...),
}}}
apiCaller := successAPICaller(c, "ProviderAddresses", entitiesArgs, results)
machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive)
addrs, err := machine.ProviderAddresses()
c.Check(err, jc.ErrorIsNil)
c.Check(addrs, jc.DeepEquals, addresses)
c.Check(apiCaller.CallCount, gc.Equals, 1)
}

func (s *MachineSuite) TestSetProviderAddressesSuccess(c *gc.C) {
addresses := network.NewProviderAddresses("2001:db8::1", "0.1.2.3")
expectArgs := params.SetMachinesAddresses{
MachineAddresses: []params.MachineAddresses{{
Tag: "machine-42",
Addresses: params.FromProviderAddresses(addresses...),
}}}
results := params.ErrorResults{
Results: []params.ErrorResult{{Error: nil}},
func (s *MachineSuite) TestSetProviderNetworkConfigSuccess(c *gc.C) {
cfg := []network.InterfaceInfo{{
DeviceIndex: 0,
CIDR: "10.0.0.0/24",
Addresses: []network.ProviderAddress{
network.NewProviderAddress("10.0.0.42"),
},
}}
expectArgs := params.SetProviderNetworkConfig{
Args: []params.ProviderNetworkConfig{{
Tag: "machine-42",
Configs: params.NetworkConfigFromInterfaceInfo(cfg),
}},
}
results := params.SetProviderNetworkConfigResults{
Results: []params.SetProviderNetworkConfigResult{{Error: nil}},
}
apiCaller := successAPICaller(c, "SetProviderAddresses", expectArgs, results)
apiCaller := successAPICaller(c, "SetProviderNetworkConfig", expectArgs, results)
machine := instancepoller.NewMachine(apiCaller, s.tag, life.Alive)
err := machine.SetProviderAddresses(addresses...)
_, _, err := machine.SetProviderNetworkConfig(cfg)
c.Check(err, jc.ErrorIsNil)
c.Check(apiCaller.CallCount, gc.Equals, 1)
}
Expand Down
2 changes: 1 addition & 1 deletion apiserver/allfacades.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func AllFacades() *facade.Registry {
reg("InstanceMutater", 1, instancemutater.NewFacadeV1)
reg("InstanceMutater", 2, instancemutater.NewFacadeV2)

reg("InstancePoller", 3, instancepoller.NewFacade)
reg("InstancePoller", 4, instancepoller.NewFacade)
reg("KeyManager", 1, keymanager.NewKeyManagerAPI)
reg("KeyUpdater", 1, keyupdater.NewKeyUpdaterAPI)

Expand Down
29 changes: 9 additions & 20 deletions apiserver/common/networkingcommon/networkconfigapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/juju/juju/apiserver/common"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/core/instance"
"github.com/juju/juju/core/network"
"github.com/juju/juju/state"
"github.com/juju/juju/state/stateenvirons"
)
Expand Down Expand Up @@ -52,26 +53,13 @@ func (api *NetworkConfigAPI) SetObservedNetworkConfig(args params.SetMachineNetw
return nil
}

providerConfig, err := api.getOneMachineProviderNetworkConfig(m)
if errors.IsNotProvisioned(err) {
logger.Infof("not updating machine %q network config: %v", m.Id(), err)
return nil
}
if err != nil {
return errors.Trace(err)
}
mergedConfig := observedConfig
if len(providerConfig) != 0 {
mergedConfig = MergeProviderAndObservedNetworkConfigs(providerConfig, observedConfig)
logger.Tracef("merged observed and provider network config for machine %q: %+v", m.Id(), mergedConfig)
}

mergedConfig, err = api.fixUpFanSubnets(mergedConfig)
mergedConfig, err := api.fixUpFanSubnets(observedConfig)
if err != nil {
return errors.Trace(err)
}

return api.setOneMachineNetworkConfig(m, mergedConfig)
ifaces := params.InterfaceInfoFromNetworkConfig(mergedConfig)
return api.setLinkLayerDevicesAndAddresses(m, ifaces)
}

// fixUpFanSubnets takes network config and updates FAN subnets with proper CIDR, providerId and providerSubnetId.
Expand Down Expand Up @@ -137,7 +125,8 @@ func (api *NetworkConfigAPI) SetProviderNetworkConfig(args params.Entities) (par
}
logger.Tracef("provider network config for %q: %+v", m.Id(), providerConfig)

if err := api.setOneMachineNetworkConfig(m, providerConfig); err != nil {
ifaces := params.InterfaceInfoFromNetworkConfig(providerConfig)
if err := api.setLinkLayerDevicesAndAddresses(m, ifaces); err != nil {
result.Results[i].Error = common.ServerError(err)
continue
}
Expand Down Expand Up @@ -234,10 +223,10 @@ func (api *NetworkConfigAPI) getOneMachineProviderNetworkConfig(m *state.Machine
return providerConfig, nil
}

func (api *NetworkConfigAPI) setOneMachineNetworkConfig(
m *state.Machine, networkConfig []params.NetworkConfig,
func (api *NetworkConfigAPI) setLinkLayerDevicesAndAddresses(
m *state.Machine, ifaces []network.InterfaceInfo,
) error {
devicesArgs, devicesAddrs := NetworkConfigsToStateArgs(networkConfig)
devicesArgs, devicesAddrs := NetworkInterfacesToStateArgs(ifaces)

logger.Debugf("setting devices: %+v", devicesArgs)
if err := m.SetParentLinkLayerDevicesBeforeTheirChildren(devicesArgs); err != nil {
Expand Down
68 changes: 33 additions & 35 deletions apiserver/common/networkingcommon/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,64 +155,62 @@ func BackingSubnetToParamsSubnet(subnet BackingSubnet) params.Subnet {
}
}

// NetworkConfigsToStateArgs splits the given networkConfig into a slice of
// state.LinkLayerDeviceArgs and a slice of state.LinkLayerDeviceAddress. The
// input is expected to come from MergeProviderAndObservedNetworkConfigs and to
// be sorted.
func NetworkConfigsToStateArgs(networkConfig []params.NetworkConfig) (
// NetworkInterfacesToStateArgs splits the given interface list into a slice of
// state.LinkLayerDeviceArgs and a slice of state.LinkLayerDeviceAddress.
func NetworkInterfacesToStateArgs(ifaces []corenetwork.InterfaceInfo) (
[]state.LinkLayerDeviceArgs,
[]state.LinkLayerDeviceAddress,
) {
var devicesArgs []state.LinkLayerDeviceArgs
var devicesAddrs []state.LinkLayerDeviceAddress

logger.Tracef("transforming network config to state args: %+v", networkConfig)
logger.Tracef("transforming network interface list to state args: %+v", ifaces)
seenDeviceNames := set.NewStrings()
for _, netConfig := range networkConfig {
logger.Tracef("transforming device %q", netConfig.InterfaceName)
if !seenDeviceNames.Contains(netConfig.InterfaceName) {
for _, iface := range ifaces {
logger.Tracef("transforming device %q", iface.InterfaceName)
if !seenDeviceNames.Contains(iface.InterfaceName) {
// First time we see this, add it to devicesArgs.
seenDeviceNames.Add(netConfig.InterfaceName)
seenDeviceNames.Add(iface.InterfaceName)
var mtu uint
if netConfig.MTU >= 0 {
mtu = uint(netConfig.MTU)
if iface.MTU >= 0 {
mtu = uint(iface.MTU)
}
args := state.LinkLayerDeviceArgs{
Name: netConfig.InterfaceName,
Name: iface.InterfaceName,
MTU: mtu,
ProviderID: corenetwork.Id(netConfig.ProviderId),
Type: corenetwork.LinkLayerDeviceType(netConfig.InterfaceType),
MACAddress: netConfig.MACAddress,
IsAutoStart: !netConfig.NoAutoStart,
IsUp: !netConfig.Disabled,
ParentName: netConfig.ParentInterfaceName,
ProviderID: corenetwork.Id(iface.ProviderId),
Type: corenetwork.LinkLayerDeviceType(iface.InterfaceType),
MACAddress: iface.MACAddress,
IsAutoStart: !iface.NoAutoStart,
IsUp: !iface.Disabled,
ParentName: iface.ParentInterfaceName,
}
logger.Tracef("state device args for device: %+v", args)
devicesArgs = append(devicesArgs, args)
}

if netConfig.CIDR == "" || netConfig.Address == "" {
if iface.CIDR == "" || iface.PrimaryAddress().Value == "" {
logger.Tracef(
"skipping empty CIDR %q and/or Address %q of %q",
netConfig.CIDR, netConfig.Address, netConfig.InterfaceName,
iface.CIDR, iface.PrimaryAddress(), iface.InterfaceName,
)
continue
}
_, ipNet, err := net.ParseCIDR(netConfig.CIDR)
_, ipNet, err := net.ParseCIDR(iface.CIDR)
if err != nil {
logger.Warningf("FIXME: ignoring unexpected CIDR format %q: %v", netConfig.CIDR, err)
logger.Warningf("FIXME: ignoring unexpected CIDR format %q: %v", iface.CIDR, err)
continue
}
ipAddr := net.ParseIP(netConfig.Address)
ipAddr := net.ParseIP(iface.PrimaryAddress().Value)
if ipAddr == nil {
logger.Warningf("FIXME: ignoring unexpected Address format %q", netConfig.Address)
logger.Warningf("FIXME: ignoring unexpected Address format %q", iface.PrimaryAddress().Value)
continue
}
ipNet.IP = ipAddr
cidrAddress := ipNet.String()

var derivedConfigMethod state.AddressConfigMethod
switch method := state.AddressConfigMethod(netConfig.ConfigType); method {
switch method := state.AddressConfigMethod(iface.ConfigType); method {
case state.StaticAddress, state.DynamicAddress,
state.LoopbackAddress, state.ManualAddress:
derivedConfigMethod = method
Expand All @@ -223,22 +221,22 @@ func NetworkConfigsToStateArgs(networkConfig []params.NetworkConfig) (
}

addr := state.LinkLayerDeviceAddress{
DeviceName: netConfig.InterfaceName,
ProviderID: corenetwork.Id(netConfig.ProviderAddressId),
ProviderNetworkID: corenetwork.Id(netConfig.ProviderNetworkId),
ProviderSubnetID: corenetwork.Id(netConfig.ProviderSubnetId),
DeviceName: iface.InterfaceName,
ProviderID: corenetwork.Id(iface.ProviderAddressId),
ProviderNetworkID: corenetwork.Id(iface.ProviderNetworkId),
ProviderSubnetID: corenetwork.Id(iface.ProviderSubnetId),
ConfigMethod: derivedConfigMethod,
CIDRAddress: cidrAddress,
DNSServers: netConfig.DNSServers,
DNSSearchDomains: netConfig.DNSSearchDomains,
GatewayAddress: netConfig.GatewayAddress,
IsDefaultGateway: netConfig.IsDefaultGateway,
DNSServers: iface.DNSServers.ToIPAddresses(),
DNSSearchDomains: iface.DNSSearchDomains,
GatewayAddress: iface.GatewayAddress.Value,
IsDefaultGateway: iface.IsDefaultGateway,
}
logger.Tracef("state address args for device: %+v", addr)
devicesAddrs = append(devicesAddrs, addr)
}
logger.Tracef("seen devices: %+v", seenDeviceNames.SortedValues())
logger.Tracef("network config transformed to state args:\n%+v\n%+v", devicesArgs, devicesAddrs)
logger.Tracef("network interface list transformed to state args:\n%+v\n%+v", devicesArgs, devicesAddrs)
return devicesArgs, devicesAddrs
}

Expand Down
5 changes: 3 additions & 2 deletions apiserver/common/networkingcommon/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -883,8 +883,9 @@ var expectedLinkLayerDeviceAdressesWithFinalNetworkConfig = []state.LinkLayerDev
ProviderSubnetID: "11",
}}

func (s *TypesSuite) TestNetworkConfigsToStateArgs(c *gc.C) {
devicesArgs, devicesAddrs := networkingcommon.NetworkConfigsToStateArgs(expectedFinalNetworkConfigs)
func (s *TypesSuite) TestNetworkInterfacesToStateArgs(c *gc.C) {
ifaces := params.InterfaceInfoFromNetworkConfig(expectedFinalNetworkConfigs)
devicesArgs, devicesAddrs := networkingcommon.NetworkInterfacesToStateArgs(ifaces)

c.Check(devicesArgs, jc.DeepEquals, expectedLinkLayerDeviceArgsWithFinalNetworkConfig)
c.Check(devicesAddrs, jc.DeepEquals, expectedLinkLayerDeviceAdressesWithFinalNetworkConfig)
Expand Down
2 changes: 1 addition & 1 deletion apiserver/facades/agent/machine/machiner.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (api *MachinerAPI) RecordAgentStartTime(args params.Entities) (params.Error
return results, nil
}

// MachinerAPI implements the V1 API used by the machiner worker. Compared to
// MachinerAPIV1 implements the V1 API used by the machiner worker. Compared to
// V2, it lacks the RecordAgentStartTime method.
type MachinerAPIV1 struct {
*MachinerAPI
Expand Down
3 changes: 2 additions & 1 deletion apiserver/facades/agent/provisioner/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,8 @@ func (api *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.Er
return err
}

devicesArgs, devicesAddrs := networkingcommon.NetworkConfigsToStateArgs(arg.NetworkConfig)
ifaces := params.InterfaceInfoFromNetworkConfig(arg.NetworkConfig)
devicesArgs, devicesAddrs := networkingcommon.NetworkInterfacesToStateArgs(ifaces)

err = machine.SetInstanceInfo(
arg.InstanceId, arg.DisplayName, arg.Nonce, arg.Characteristics,
Expand Down
Loading

0 comments on commit 2791d2f

Please sign in to comment.