github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/container/lxd/network_test.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lxd_test
     5  
     6  import (
     7  	"errors"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	jc "github.com/juju/testing/checkers"
    11  	lxdapi "github.com/lxc/lxd/shared/api"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/container/lxd"
    15  	lxdtesting "github.com/juju/juju/container/lxd/testing"
    16  	"github.com/juju/juju/network"
    17  )
    18  
    19  type networkSuite struct {
    20  	lxdtesting.BaseSuite
    21  }
    22  
    23  var _ = gc.Suite(&networkSuite{})
    24  
    25  func (s *networkSuite) patch() {
    26  	lxd.PatchGenerateVirtualMACAddress(s)
    27  }
    28  
    29  func defaultProfileWithNIC() *lxdapi.Profile {
    30  	return &lxdapi.Profile{
    31  		Name: "default",
    32  		ProfilePut: lxdapi.ProfilePut{
    33  			Devices: map[string]map[string]string{
    34  				"eth0": {
    35  					"parent":  network.DefaultLXDBridge,
    36  					"type":    "nic",
    37  					"nictype": "bridged",
    38  				},
    39  			},
    40  		},
    41  	}
    42  }
    43  
    44  func (s *networkSuite) TestEnsureIPv4NoChange(c *gc.C) {
    45  	ctrl := gomock.NewController(c)
    46  	defer ctrl.Finish()
    47  	cSvr := s.NewMockServerWithExtensions(ctrl, "network")
    48  
    49  	net := &lxdapi.Network{
    50  		NetworkPut: lxdapi.NetworkPut{
    51  			Config: map[string]string{
    52  				"ipv4.address": "10.5.3.1",
    53  			},
    54  		},
    55  	}
    56  	cSvr.EXPECT().GetNetwork("some-net-name").Return(net, lxdtesting.ETag, nil)
    57  
    58  	jujuSvr, err := lxd.NewServer(cSvr)
    59  	c.Assert(err, jc.ErrorIsNil)
    60  
    61  	mod, err := jujuSvr.EnsureIPv4("some-net-name")
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	c.Check(mod, jc.IsFalse)
    64  }
    65  
    66  func (s *networkSuite) TestEnsureIPv4Modified(c *gc.C) {
    67  	ctrl := gomock.NewController(c)
    68  	defer ctrl.Finish()
    69  	cSvr := s.NewMockServerWithExtensions(ctrl, "network")
    70  
    71  	req := lxdapi.NetworkPut{
    72  		Config: map[string]string{
    73  			"ipv4.address": "auto",
    74  			"ipv4.nat":     "true",
    75  		},
    76  	}
    77  	gomock.InOrder(
    78  		cSvr.EXPECT().GetNetwork(network.DefaultLXDBridge).Return(&lxdapi.Network{}, lxdtesting.ETag, nil),
    79  		cSvr.EXPECT().UpdateNetwork(network.DefaultLXDBridge, req, lxdtesting.ETag).Return(nil),
    80  	)
    81  
    82  	jujuSvr, err := lxd.NewServer(cSvr)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  
    85  	mod, err := jujuSvr.EnsureIPv4(network.DefaultLXDBridge)
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	c.Check(mod, jc.IsTrue)
    88  }
    89  
    90  func (s *networkSuite) TestGetNICsFromProfile(c *gc.C) {
    91  	lxd.PatchGenerateVirtualMACAddress(s)
    92  
    93  	ctrl := gomock.NewController(c)
    94  	defer ctrl.Finish()
    95  	cSvr := s.NewMockServer(ctrl)
    96  
    97  	cSvr.EXPECT().GetProfile("default").Return(defaultProfileWithNIC(), lxdtesting.ETag, nil)
    98  
    99  	jujuSvr, err := lxd.NewServer(cSvr)
   100  	c.Assert(err, jc.ErrorIsNil)
   101  
   102  	nics, err := jujuSvr.GetNICsFromProfile("default")
   103  	c.Assert(err, jc.ErrorIsNil)
   104  
   105  	exp := map[string]map[string]string{
   106  		"eth0": {
   107  			"parent":  network.DefaultLXDBridge,
   108  			"type":    "nic",
   109  			"nictype": "bridged",
   110  			"hwaddr":  "00:16:3e:00:00:00",
   111  		},
   112  	}
   113  
   114  	c.Check(nics, gc.DeepEquals, exp)
   115  }
   116  
   117  func (s *networkSuite) TestVerifyNetworkDevicePresentValid(c *gc.C) {
   118  	ctrl := gomock.NewController(c)
   119  	defer ctrl.Finish()
   120  	cSvr := s.NewMockServerWithExtensions(ctrl, "network")
   121  
   122  	cSvr.EXPECT().GetNetwork(network.DefaultLXDBridge).Return(&lxdapi.Network{}, "", nil)
   123  
   124  	jujuSvr, err := lxd.NewServer(cSvr)
   125  	c.Assert(err, jc.ErrorIsNil)
   126  
   127  	err = jujuSvr.VerifyNetworkDevice(defaultProfileWithNIC(), "")
   128  	c.Assert(err, jc.ErrorIsNil)
   129  }
   130  
   131  func (s *networkSuite) TestVerifyNetworkDeviceMultipleNICsOneValid(c *gc.C) {
   132  	ctrl := gomock.NewController(c)
   133  	defer ctrl.Finish()
   134  	cSvr := s.NewMockServerClustered(ctrl, "cluster-1")
   135  
   136  	profile := defaultProfileWithNIC()
   137  	profile.Devices["eno1"] = profile.Devices["eth0"]
   138  	profile.Devices["eno1"]["parent"] = "valid-net"
   139  
   140  	net := &lxdapi.Network{
   141  		Name:    network.DefaultLXDBridge,
   142  		Managed: true,
   143  		NetworkPut: lxdapi.NetworkPut{
   144  			Config: map[string]string{
   145  				"ipv6.address": "something-not-nothing",
   146  			},
   147  		},
   148  	}
   149  
   150  	// Random map iteration may or may not cause this call to be made.
   151  	cSvr.EXPECT().GetNetwork(network.DefaultLXDBridge).Return(net, "", nil).MaxTimes(1)
   152  	cSvr.EXPECT().GetNetwork("valid-net").Return(&lxdapi.Network{}, "", nil)
   153  
   154  	jujuSvr, err := lxd.NewServer(cSvr)
   155  	c.Assert(err, jc.ErrorIsNil)
   156  
   157  	err = jujuSvr.VerifyNetworkDevice(profile, "")
   158  	c.Assert(err, jc.ErrorIsNil)
   159  
   160  	c.Check(jujuSvr.LocalBridgeName(), gc.Equals, "valid-net")
   161  }
   162  
   163  func (s *networkSuite) TestVerifyNetworkDevicePresentBadNicType(c *gc.C) {
   164  	ctrl := gomock.NewController(c)
   165  	defer ctrl.Finish()
   166  	cSvr := s.NewMockServerWithExtensions(ctrl, "network")
   167  
   168  	profile := defaultProfileWithNIC()
   169  	profile.Devices["eth0"]["nictype"] = "not-bridge-or-macvlan"
   170  
   171  	jujuSvr, err := lxd.NewServer(cSvr)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  
   174  	err = jujuSvr.VerifyNetworkDevice(profile, "")
   175  	c.Assert(err, gc.ErrorMatches,
   176  		`profile "default": no network device found with nictype "bridged" or "macvlan"\n`+
   177  			`\tthe following devices were checked: eth0\n`+
   178  			`Note: juju does not support IPv6.\n`+
   179  			`Reconfigure lxd to use a network of type "bridged" or "macvlan", disabling IPv6.`)
   180  }
   181  
   182  func (s *networkSuite) TestVerifyNetworkDeviceIPv6Present(c *gc.C) {
   183  	ctrl := gomock.NewController(c)
   184  	defer ctrl.Finish()
   185  	cSvr := s.NewMockServerWithExtensions(ctrl, "network")
   186  
   187  	net := &lxdapi.Network{
   188  		Name:    network.DefaultLXDBridge,
   189  		Managed: true,
   190  		NetworkPut: lxdapi.NetworkPut{
   191  			Config: map[string]string{
   192  				"ipv6.address": "something-not-nothing",
   193  			},
   194  		},
   195  	}
   196  	cSvr.EXPECT().GetNetwork(network.DefaultLXDBridge).Return(net, "", nil)
   197  
   198  	jujuSvr, err := lxd.NewServer(cSvr)
   199  	c.Assert(err, jc.ErrorIsNil)
   200  
   201  	err = jujuSvr.VerifyNetworkDevice(defaultProfileWithNIC(), "")
   202  	c.Assert(err, gc.ErrorMatches,
   203  		`profile "default": juju does not support IPv6. Disable IPv6 in LXD via:\n`+
   204  			`\tlxc network set lxdbr0 ipv6.address none\n`+
   205  			`and run the command again`)
   206  }
   207  
   208  func (s *networkSuite) TestVerifyNetworkDeviceNotPresentCreated(c *gc.C) {
   209  	s.patch()
   210  
   211  	ctrl := gomock.NewController(c)
   212  	defer ctrl.Finish()
   213  	cSvr := s.NewMockServerWithExtensions(ctrl, "network")
   214  
   215  	netConf := map[string]string{
   216  		"ipv4.address": "auto",
   217  		"ipv4.nat":     "true",
   218  		"ipv6.address": "none",
   219  		"ipv6.nat":     "false",
   220  	}
   221  	netCreateReq := lxdapi.NetworksPost{
   222  		Name:       network.DefaultLXDBridge,
   223  		Type:       "bridge",
   224  		Managed:    true,
   225  		NetworkPut: lxdapi.NetworkPut{Config: netConf},
   226  	}
   227  	newNet := &lxdapi.Network{
   228  		Name:       network.DefaultLXDBridge,
   229  		Type:       "bridge",
   230  		Managed:    true,
   231  		NetworkPut: lxdapi.NetworkPut{Config: netConf},
   232  	}
   233  	gomock.InOrder(
   234  		cSvr.EXPECT().GetNetwork(network.DefaultLXDBridge).Return(nil, "", errors.New("not found")),
   235  		cSvr.EXPECT().CreateNetwork(netCreateReq).Return(nil),
   236  		cSvr.EXPECT().GetNetwork(network.DefaultLXDBridge).Return(newNet, "", nil),
   237  		cSvr.EXPECT().UpdateProfile("default", defaultProfileWithNIC().Writable(), lxdtesting.ETag).Return(nil),
   238  	)
   239  
   240  	profile := defaultProfileWithNIC()
   241  	delete(profile.Devices, "eth0")
   242  
   243  	jujuSvr, err := lxd.NewServer(cSvr)
   244  	c.Assert(err, jc.ErrorIsNil)
   245  
   246  	err = jujuSvr.VerifyNetworkDevice(profile, lxdtesting.ETag)
   247  	c.Assert(err, jc.ErrorIsNil)
   248  }
   249  
   250  func (s *networkSuite) TestVerifyNetworkDeviceNotPresentCreatedWithUnusedName(c *gc.C) {
   251  	s.patch()
   252  
   253  	ctrl := gomock.NewController(c)
   254  	defer ctrl.Finish()
   255  	cSvr := s.NewMockServerWithExtensions(ctrl, "network")
   256  
   257  	defaultBridge := &lxdapi.Network{
   258  		Name:    network.DefaultLXDBridge,
   259  		Type:    "bridge",
   260  		Managed: true,
   261  		NetworkPut: lxdapi.NetworkPut{
   262  			Config: map[string]string{
   263  				"ipv4.address": "auto",
   264  				"ipv4.nat":     "true",
   265  				"ipv6.address": "none",
   266  				"ipv6.nat":     "false",
   267  			},
   268  		},
   269  	}
   270  	devReq := lxdapi.ProfilePut{
   271  		Devices: map[string]map[string]string{
   272  			"eth0": {},
   273  			"eth1": {},
   274  			// eth2 will be generated as the first unused device name.
   275  			"eth2": {
   276  				"parent":  network.DefaultLXDBridge,
   277  				"type":    "nic",
   278  				"nictype": "bridged",
   279  			},
   280  		},
   281  	}
   282  	gomock.InOrder(
   283  		cSvr.EXPECT().GetNetwork(network.DefaultLXDBridge).Return(defaultBridge, "", nil),
   284  		cSvr.EXPECT().UpdateProfile("default", devReq, lxdtesting.ETag).Return(nil),
   285  	)
   286  
   287  	profile := defaultProfileWithNIC()
   288  	profile.Devices["eth0"] = map[string]string{}
   289  	profile.Devices["eth1"] = map[string]string{}
   290  
   291  	jujuSvr, err := lxd.NewServer(cSvr)
   292  	c.Assert(err, jc.ErrorIsNil)
   293  
   294  	err = jujuSvr.VerifyNetworkDevice(profile, lxdtesting.ETag)
   295  	c.Assert(err, jc.ErrorIsNil)
   296  }
   297  
   298  func (s *networkSuite) TestVerifyNetworkDeviceNotPresentErrorForCluster(c *gc.C) {
   299  	ctrl := gomock.NewController(c)
   300  	defer ctrl.Finish()
   301  	cSvr := s.NewMockServerClustered(ctrl, "cluster-1")
   302  
   303  	profile := defaultProfileWithNIC()
   304  	delete(profile.Devices, "eth0")
   305  
   306  	jujuSvr, err := lxd.NewServer(cSvr)
   307  	c.Assert(err, jc.ErrorIsNil)
   308  
   309  	err = jujuSvr.VerifyNetworkDevice(profile, lxdtesting.ETag)
   310  	c.Assert(err, gc.ErrorMatches, `profile "default" does not have any devices configured with type "nic"`)
   311  }
   312  
   313  func (s *networkSuite) TestInterfaceInfoFromDevices(c *gc.C) {
   314  	nics := map[string]map[string]string{
   315  		"eth0": {
   316  			"parent":  network.DefaultLXDBridge,
   317  			"type":    "nic",
   318  			"nictype": "bridged",
   319  			"hwaddr":  "00:16:3e:00:00:00",
   320  		},
   321  		"eno9": {
   322  			"parent":  "br1",
   323  			"type":    "nic",
   324  			"nictype": "macvlan",
   325  			"hwaddr":  "00:16:3e:00:00:3e",
   326  		},
   327  	}
   328  
   329  	info, err := lxd.InterfaceInfoFromDevices(nics)
   330  	c.Assert(err, jc.ErrorIsNil)
   331  
   332  	exp := []network.InterfaceInfo{
   333  		{
   334  			InterfaceName:       "eno9",
   335  			MACAddress:          "00:16:3e:00:00:3e",
   336  			ConfigType:          network.ConfigDHCP,
   337  			ParentInterfaceName: "br1",
   338  		},
   339  		{
   340  			InterfaceName:       "eth0",
   341  			MACAddress:          "00:16:3e:00:00:00",
   342  			ConfigType:          network.ConfigDHCP,
   343  			ParentInterfaceName: network.DefaultLXDBridge,
   344  		},
   345  	}
   346  	c.Check(info, jc.DeepEquals, exp)
   347  }
   348  
   349  func (s *networkSuite) TestCheckLXDBridgeConfiguration(c *gc.C) {
   350  	bridgeName, err := lxd.CheckBridgeConfigFile(validBridgeConfig)
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	c.Check(bridgeName, gc.Equals, "lxdbr0")
   353  
   354  	noBridge := func(string) ([]byte, error) {
   355  		return []byte(`
   356  USE_LXD_BRIDGE="false"
   357  `), nil
   358  	}
   359  	_, err = lxd.CheckBridgeConfigFile(noBridge)
   360  	c.Assert(err.Error(), gc.Equals, `/etc/default/lxd-bridge has USE_LXD_BRIDGE set to false
   361  It looks like your LXD bridge has not yet been configured. Configure it via:
   362  
   363  	sudo dpkg-reconfigure -p medium lxd
   364  
   365  and run the command again.`)
   366  
   367  	noSubnets := func(string) ([]byte, error) {
   368  		return []byte(`
   369  USE_LXD_BRIDGE="true"
   370  LXD_BRIDGE="br0"
   371  `), nil
   372  	}
   373  	_, err = lxd.CheckBridgeConfigFile(noSubnets)
   374  	c.Assert(err.Error(), gc.Equals, `br0 has no ipv4 or ipv6 subnet enabled
   375  It looks like your LXD bridge has not yet been configured. Configure it via:
   376  
   377  	sudo dpkg-reconfigure -p medium lxd
   378  
   379  and run the command again.`)
   380  
   381  	ipv6 := func(string) ([]byte, error) {
   382  		return []byte(`
   383  USE_LXD_BRIDGE="true"
   384  LXD_BRIDGE="lxdbr0"
   385  LXD_IPV6_ADDR="2001:470:b368:4242::1"
   386  `), nil
   387  	}
   388  
   389  	_, err = lxd.CheckBridgeConfigFile(ipv6)
   390  	c.Assert(err.Error(), gc.Equals, lxd.BridgeConfigFile+` has IPv6 enabled.
   391  Juju doesn't currently support IPv6.
   392  Disable IPv6 via:
   393  
   394  	sudo dpkg-reconfigure -p medium lxd
   395  
   396  and run the command again.`)
   397  }
   398  
   399  func (s *networkSuite) TestVerifyNICsWithConfigFileNICFound(c *gc.C) {
   400  	ctrl := gomock.NewController(c)
   401  	defer ctrl.Finish()
   402  	cSvr := s.NewMockServer(ctrl)
   403  
   404  	jujuSvr, err := lxd.NewServer(cSvr)
   405  	c.Assert(err, jc.ErrorIsNil)
   406  
   407  	err = lxd.VerifyNICsWithConfigFile(jujuSvr, defaultProfileWithNIC().Devices, validBridgeConfig)
   408  	c.Assert(err, jc.ErrorIsNil)
   409  	c.Check(jujuSvr.LocalBridgeName(), gc.Equals, "lxdbr0")
   410  }
   411  
   412  func (s *networkSuite) TestVerifyNICsWithConfigFileNICNotFound(c *gc.C) {
   413  	ctrl := gomock.NewController(c)
   414  	defer ctrl.Finish()
   415  	cSvr := s.NewMockServer(ctrl)
   416  
   417  	jujuSvr, err := lxd.NewServer(cSvr)
   418  	c.Assert(err, jc.ErrorIsNil)
   419  
   420  	nics := defaultProfileWithNIC().Devices
   421  	nics["eth0"]["parent"] = "br0"
   422  
   423  	err = lxd.VerifyNICsWithConfigFile(jujuSvr, nics, validBridgeConfig)
   424  	c.Assert(err, gc.ErrorMatches,
   425  		`no network device found with nictype "bridged" or "macvlan" that uses the configured bridge in `+
   426  			lxd.BridgeConfigFile+"\n\tthe following devices were checked: "+`\[eth0\]`)
   427  }
   428  
   429  func validBridgeConfig(_ string) ([]byte, error) {
   430  	return []byte(`
   431  # Whether to setup a new bridge or use an existing one
   432  USE_LXD_BRIDGE="true"
   433  
   434  # Bridge name
   435  # This is still used even if USE_LXD_BRIDGE is set to false
   436  # set to an empty value to fully disable
   437  LXD_BRIDGE="lxdbr0"
   438  
   439  # Path to an extra dnsmasq configuration file
   440  LXD_CONFILE=""
   441  
   442  # DNS domain for the bridge
   443  LXD_DOMAIN="lxd"
   444  
   445  # IPv4
   446  ## IPv4 address (e.g. 10.0.4.1)
   447  LXD_IPV4_ADDR="10.0.4.1"
   448  
   449  ## IPv4 netmask (e.g. 255.255.255.0)
   450  LXD_IPV4_NETMASK="255.255.255.0"
   451  
   452  ## IPv4 network (e.g. 10.0.4.0/24)
   453  LXD_IPV4_NETWORK="10.0.4.1/24"
   454  
   455  ## IPv4 DHCP range (e.g. 10.0.4.2,10.0.4.254)
   456  LXD_IPV4_DHCP_RANGE="10.0.4.2,10.0.4.254"
   457  
   458  ## IPv4 DHCP number of hosts (e.g. 250)
   459  LXD_IPV4_DHCP_MAX="253"
   460  
   461  ## NAT IPv4 traffic
   462  LXD_IPV4_NAT="true"
   463  
   464  # IPv6
   465  ## IPv6 address (e.g. 2001:470:b368:4242::1)
   466  LXD_IPV6_ADDR=""
   467  
   468  ## IPv6 CIDR mask (e.g. 64)
   469  LXD_IPV6_MASK=""
   470  LXD_IPV6_NETWORK=""
   471  `), nil
   472  }
   473  
   474  func (s *networkSuite) TestEnableHTTPSListener(c *gc.C) {
   475  	ctrl := gomock.NewController(c)
   476  	defer ctrl.Finish()
   477  
   478  	cfg := &lxdapi.Server{}
   479  	cSvr := lxdtesting.NewMockContainerServer(ctrl)
   480  
   481  	gomock.InOrder(
   482  		cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, nil).Times(2),
   483  		cSvr.EXPECT().UpdateServer(lxdapi.ServerPut{
   484  			Config: map[string]interface{}{
   485  				"core.https_address": "[::]",
   486  			},
   487  		}, lxdtesting.ETag).Return(nil),
   488  	)
   489  
   490  	jujuSvr, err := lxd.NewServer(cSvr)
   491  	c.Assert(err, jc.ErrorIsNil)
   492  
   493  	err = jujuSvr.EnableHTTPSListener()
   494  	c.Assert(err, jc.ErrorIsNil)
   495  }
   496  
   497  func (s *networkSuite) TestEnableHTTPSListenerWithFallbackToIPv4(c *gc.C) {
   498  	ctrl := gomock.NewController(c)
   499  	defer ctrl.Finish()
   500  
   501  	cfg := &lxdapi.Server{}
   502  	cSvr := lxdtesting.NewMockContainerServer(ctrl)
   503  
   504  	gomock.InOrder(
   505  		cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, nil).Times(2),
   506  		cSvr.EXPECT().UpdateServer(lxdapi.ServerPut{
   507  			Config: map[string]interface{}{
   508  				"core.https_address": "[::]",
   509  			},
   510  		}, lxdtesting.ETag).Return(errors.New(lxd.ErrIPV6NotSupported)),
   511  		cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, nil),
   512  		cSvr.EXPECT().UpdateServer(lxdapi.ServerPut{
   513  			Config: map[string]interface{}{
   514  				"core.https_address": "0.0.0.0",
   515  			},
   516  		}, lxdtesting.ETag).Return(nil),
   517  	)
   518  
   519  	jujuSvr, err := lxd.NewServer(cSvr)
   520  	c.Assert(err, jc.ErrorIsNil)
   521  
   522  	err = jujuSvr.EnableHTTPSListener()
   523  	c.Assert(err, jc.ErrorIsNil)
   524  }
   525  
   526  func (s *networkSuite) TestEnableHTTPSListenerWithErrors(c *gc.C) {
   527  	ctrl := gomock.NewController(c)
   528  	defer ctrl.Finish()
   529  
   530  	cfg := &lxdapi.Server{}
   531  	cSvr := lxdtesting.NewMockContainerServer(ctrl)
   532  
   533  	cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, nil)
   534  
   535  	jujuSvr, err := lxd.NewServer(cSvr)
   536  	c.Assert(err, jc.ErrorIsNil)
   537  
   538  	// check on the first request
   539  	cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, errors.New("bad"))
   540  
   541  	err = jujuSvr.EnableHTTPSListener()
   542  	c.Assert(err, gc.ErrorMatches, "bad")
   543  
   544  	// check on the second request
   545  	gomock.InOrder(
   546  		cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, nil),
   547  		cSvr.EXPECT().UpdateServer(lxdapi.ServerPut{
   548  			Config: map[string]interface{}{
   549  				"core.https_address": "[::]",
   550  			},
   551  		}, lxdtesting.ETag).Return(errors.New(lxd.ErrIPV6NotSupported)),
   552  		cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, errors.New("bad")),
   553  	)
   554  
   555  	err = jujuSvr.EnableHTTPSListener()
   556  	c.Assert(err, gc.ErrorMatches, "bad")
   557  
   558  	// check on the third request
   559  	gomock.InOrder(
   560  		cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, nil),
   561  		cSvr.EXPECT().UpdateServer(lxdapi.ServerPut{
   562  			Config: map[string]interface{}{
   563  				"core.https_address": "[::]",
   564  			},
   565  		}, lxdtesting.ETag).Return(errors.New(lxd.ErrIPV6NotSupported)),
   566  		cSvr.EXPECT().GetServer().Return(cfg, lxdtesting.ETag, nil),
   567  		cSvr.EXPECT().UpdateServer(lxdapi.ServerPut{
   568  			Config: map[string]interface{}{
   569  				"core.https_address": "0.0.0.0",
   570  			},
   571  		}, lxdtesting.ETag).Return(errors.New("bad")),
   572  	)
   573  
   574  	err = jujuSvr.EnableHTTPSListener()
   575  	c.Assert(err, gc.ErrorMatches, "bad")
   576  }
   577  
   578  func (s *networkSuite) TestNewNICDeviceWithoutMACAddressOrMTUGreaterThanZero(c *gc.C) {
   579  	device := lxd.NewNICDevice("eth1", "br-eth1", "", 0)
   580  	expected := map[string]string{
   581  		"name":    "eth1",
   582  		"nictype": "bridged",
   583  		"parent":  "br-eth1",
   584  		"type":    "nic",
   585  	}
   586  	c.Assert(device, gc.DeepEquals, expected)
   587  }
   588  
   589  func (s *networkSuite) TestNewNICDeviceWithMACAddressButNoMTU(c *gc.C) {
   590  	device := lxd.NewNICDevice("eth1", "br-eth1", "aa:bb:cc:dd:ee:f0", 0)
   591  	expected := map[string]string{
   592  		"hwaddr":  "aa:bb:cc:dd:ee:f0",
   593  		"name":    "eth1",
   594  		"nictype": "bridged",
   595  		"parent":  "br-eth1",
   596  		"type":    "nic",
   597  	}
   598  	c.Assert(device, gc.DeepEquals, expected)
   599  }
   600  
   601  func (s *networkSuite) TestNewNICDeviceWithoutMACAddressButMTUGreaterThanZero(c *gc.C) {
   602  	device := lxd.NewNICDevice("eth1", "br-eth1", "", 1492)
   603  	expected := map[string]string{
   604  		"mtu":     "1492",
   605  		"name":    "eth1",
   606  		"nictype": "bridged",
   607  		"parent":  "br-eth1",
   608  		"type":    "nic",
   609  	}
   610  	c.Assert(device, gc.DeepEquals, expected)
   611  }
   612  
   613  func (s *networkSuite) TestNewNICDeviceWithMACAddressAndMTUGreaterThanZero(c *gc.C) {
   614  	device := lxd.NewNICDevice("eth1", "br-eth1", "aa:bb:cc:dd:ee:f0", 9000)
   615  	expected := map[string]string{
   616  		"hwaddr":  "aa:bb:cc:dd:ee:f0",
   617  		"mtu":     "9000",
   618  		"name":    "eth1",
   619  		"nictype": "bridged",
   620  		"parent":  "br-eth1",
   621  		"type":    "nic",
   622  	}
   623  	c.Assert(device, gc.DeepEquals, expected)
   624  }