github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/networker/networker_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package networker_test
     5  
     6  import (
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/juju/names"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/utils/set"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/agent"
    20  	"github.com/juju/juju/api"
    21  	apinetworker "github.com/juju/juju/api/networker"
    22  	"github.com/juju/juju/instance"
    23  	"github.com/juju/juju/juju/testing"
    24  	"github.com/juju/juju/state"
    25  	coretesting "github.com/juju/juju/testing"
    26  	"github.com/juju/juju/worker"
    27  	"github.com/juju/juju/worker/networker"
    28  )
    29  
    30  type networkerSuite struct {
    31  	testing.JujuConnSuite
    32  
    33  	stateMachine    *state.Machine
    34  	stateNetworks   []state.NetworkInfo
    35  	stateInterfaces []state.NetworkInterfaceInfo
    36  
    37  	upInterfaces          set.Strings
    38  	interfacesWithAddress set.Strings
    39  	machineInterfaces     []net.Interface
    40  	vlanModuleLoaded      bool
    41  	lastCommands          chan []string
    42  
    43  	apiState  api.Connection
    44  	apiFacade apinetworker.State
    45  }
    46  
    47  var _ = gc.Suite(&networkerSuite{})
    48  
    49  func (s *networkerSuite) SetUpTest(c *gc.C) {
    50  	s.JujuConnSuite.SetUpTest(c)
    51  
    52  	// Setup testing state.
    53  	s.setUpNetworks(c)
    54  	s.setUpMachine(c)
    55  
    56  	s.machineInterfaces = []net.Interface{
    57  		{Index: 1, MTU: 65535, Name: "lo", Flags: net.FlagUp | net.FlagLoopback},
    58  		{Index: 2, MTU: 1500, Name: "eth0", Flags: net.FlagUp},
    59  		{Index: 3, MTU: 1500, Name: "eth1"},
    60  		{Index: 4, MTU: 1500, Name: "eth2"},
    61  	}
    62  	s.PatchValue(&networker.InterfaceIsUp, func(name string) bool {
    63  		return s.upInterfaces.Contains(name)
    64  	})
    65  	s.PatchValue(&networker.InterfaceHasAddress, func(name string) bool {
    66  		return s.interfacesWithAddress.Contains(name)
    67  	})
    68  	s.PatchValue(&networker.ExecuteCommands, func(commands []string) error {
    69  		return s.executeCommandsHook(c, commands)
    70  	})
    71  	s.PatchValue(&networker.Interfaces, func() ([]net.Interface, error) {
    72  		return s.machineInterfaces, nil
    73  	})
    74  
    75  	// Create the networker API facade.
    76  	s.apiFacade = s.apiState.Networker()
    77  	c.Assert(s.apiFacade, gc.NotNil)
    78  }
    79  
    80  func (s *networkerSuite) TestStartStop(c *gc.C) {
    81  	nw := s.newNetworker(c, true)
    82  	c.Assert(worker.Stop(nw), gc.IsNil)
    83  }
    84  
    85  func (s *networkerSuite) TestConfigPaths(c *gc.C) {
    86  	nw, configDir := s.newCustomNetworker(c, s.apiFacade, s.stateMachine.Id(), true, true)
    87  	defer worker.Stop(nw)
    88  
    89  	c.Assert(nw.ConfigBaseDir(), gc.Equals, configDir)
    90  	subdir := filepath.Join(configDir, "interfaces.d")
    91  	c.Assert(nw.ConfigSubDir(), gc.Equals, subdir)
    92  	c.Assert(nw.ConfigFile(""), gc.Equals, filepath.Join(configDir, "interfaces"))
    93  	c.Assert(nw.ConfigFile("ethX.42"), gc.Equals, filepath.Join(subdir, "ethX.42.cfg"))
    94  }
    95  
    96  func (s *networkerSuite) TestSafeNetworkerCannotWriteConfig(c *gc.C) {
    97  	c.Skip("enable once the networker is enabled again")
    98  
    99  	nw := s.newNetworker(c, false)
   100  	defer worker.Stop(nw)
   101  	c.Assert(nw.IntrusiveMode(), jc.IsFalse)
   102  
   103  	select {
   104  	case cmds := <-s.lastCommands:
   105  		c.Fatalf("no commands expected, got %v", cmds)
   106  	case <-time.After(coretesting.ShortWait):
   107  		s.assertNoConfig(c, nw, "", "lo", "eth0", "eth1", "eth1.42", "eth0.69")
   108  	}
   109  }
   110  
   111  func (s *networkerSuite) TestNormalNetworkerCanWriteConfigAndLoadsVLANModule(c *gc.C) {
   112  	c.Skip("enable once the networker is enabled again")
   113  
   114  	nw := s.newNetworker(c, true)
   115  	defer worker.Stop(nw)
   116  	c.Assert(nw.IntrusiveMode(), jc.IsTrue)
   117  
   118  	select {
   119  	case <-s.lastCommands:
   120  		// VLAN module loading commands is one of the first things the
   121  		// worker does, so if it happened, we can assume commands are
   122  		// executed.
   123  		c.Assert(s.vlanModuleLoaded, jc.IsTrue)
   124  		c.Assert(nw.IsVLANModuleLoaded(), jc.IsTrue)
   125  	case <-time.After(coretesting.ShortWait):
   126  		c.Fatalf("commands expected but not executed")
   127  	}
   128  	c.Assert(nw.IsPrimaryInterfaceOrLoopback("lo"), jc.IsTrue)
   129  	c.Assert(nw.IsPrimaryInterfaceOrLoopback("eth0"), jc.IsTrue)
   130  	s.assertHaveConfig(c, nw, "", "eth0", "eth1", "eth1.42", "eth0.69")
   131  }
   132  
   133  func (s *networkerSuite) TestPrimaryOrLoopbackInterfacesAreSkipped(c *gc.C) {
   134  	c.Skip("enable once the networker is enabled again")
   135  
   136  	// Reset what's considered up, so we can test eth0 and lo are not
   137  	// touched.
   138  	s.upInterfaces = make(set.Strings)
   139  	s.interfacesWithAddress = make(set.Strings)
   140  
   141  	nw, _ := s.newCustomNetworker(c, s.apiFacade, s.stateMachine.Id(), true, false)
   142  	defer worker.Stop(nw)
   143  
   144  	timeout := time.After(coretesting.LongWait)
   145  	for {
   146  		select {
   147  		case <-s.lastCommands:
   148  			if !s.vlanModuleLoaded {
   149  				// VLAN module loading commands is one of the first things
   150  				// the worker does, so if hasn't happened, we wait a bit more.
   151  				continue
   152  			}
   153  			c.Assert(s.upInterfaces.Contains("lo"), jc.IsFalse)
   154  			c.Assert(s.upInterfaces.Contains("eth0"), jc.IsFalse)
   155  			if s.upInterfaces.Contains("eth1") {
   156  				// If we run ifup eth1, we successfully skipped lo and
   157  				// eth0.
   158  				s.assertHaveConfig(c, nw, "", "eth0", "eth1", "eth1.42", "eth0.69")
   159  				return
   160  			}
   161  		case <-timeout:
   162  			c.Fatalf("commands expected but not executed")
   163  		}
   164  	}
   165  }
   166  
   167  func (s *networkerSuite) TestDisabledInterfacesAreBroughtDown(c *gc.C) {
   168  	c.Skip("enable once the networker is enabled again")
   169  
   170  	// Simulate eth1 is up and then disable it, so we can test it's
   171  	// brought down. Also test the VLAN interface eth1.42 is also
   172  	// brought down, as it's physical interface eth1 is disabled.
   173  	s.upInterfaces = set.NewStrings("lo", "eth0", "eth1")
   174  	s.interfacesWithAddress = set.NewStrings("lo", "eth0", "eth1")
   175  	s.machineInterfaces[2].Flags |= net.FlagUp
   176  	ifaces, err := s.stateMachine.NetworkInterfaces()
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	err = ifaces[1].Disable()
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	// We verify that setting the parent physical interface to
   181  	// disabled leads to setting any VLAN intefaces depending on it to
   182  	// get disabled as well.
   183  	err = ifaces[2].Refresh()
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	c.Assert(ifaces[2].IsDisabled(), jc.IsTrue)
   186  
   187  	nw, _ := s.newCustomNetworker(c, s.apiFacade, s.stateMachine.Id(), true, false)
   188  	defer worker.Stop(nw)
   189  
   190  	timeout := time.After(coretesting.LongWait)
   191  	for {
   192  		select {
   193  		case cmds := <-s.lastCommands:
   194  			if !strings.Contains(strings.Join(cmds, " "), "ifdown") {
   195  				// No down commands yet, keep waiting.
   196  				continue
   197  			}
   198  			c.Assert(s.upInterfaces.Contains("eth1"), jc.IsFalse)
   199  			c.Assert(s.machineInterfaces[2].Flags&net.FlagUp, gc.Equals, net.Flags(0))
   200  			c.Assert(s.upInterfaces.Contains("eth1.42"), jc.IsFalse)
   201  			s.assertNoConfig(c, nw, "eth1", "eth1.42")
   202  			s.assertHaveConfig(c, nw, "", "eth0", "eth0.69")
   203  			return
   204  		case <-timeout:
   205  			c.Fatalf("commands expected but not executed")
   206  		}
   207  	}
   208  }
   209  
   210  func (s *networkerSuite) TestIsRunningInLXC(c *gc.C) {
   211  	tests := []struct {
   212  		machineId string
   213  		result    bool
   214  	}{
   215  		{"0", false},
   216  		{"1/lxc/0", true},
   217  		{"2/kvm/1", false},
   218  		{"3/lxc/0/lxc/1", true},
   219  		{"4/lxc/0/kvm/1", false},
   220  		{"5/lxc/1/kvm/1/lxc/3", true},
   221  	}
   222  	for i, t := range tests {
   223  		c.Logf("test %d: %q -> %v", i, t.machineId, t.result)
   224  		c.Check(networker.IsRunningInLXC(t.machineId), gc.Equals, t.result)
   225  	}
   226  }
   227  
   228  func (s *networkerSuite) TestNoModprobeWhenRunningInLXC(c *gc.C) {
   229  	c.Skip("enable once the networker is enabled again")
   230  
   231  	// Create a new container.
   232  	template := state.MachineTemplate{
   233  		Series: coretesting.FakeDefaultSeries,
   234  		Jobs:   []state.MachineJob{state.JobHostUnits},
   235  	}
   236  	lxcMachine, err := s.State.AddMachineInsideMachine(template, s.stateMachine.Id(), instance.LXC)
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	password, err := utils.RandomPassword()
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	err = lxcMachine.SetPassword(password)
   241  	c.Assert(err, jc.ErrorIsNil)
   242  	lxcInterfaces := []state.NetworkInterfaceInfo{{
   243  		MACAddress:    "aa:bb:cc:dd:02:f0",
   244  		InterfaceName: "eth0.123",
   245  		NetworkName:   "vlan123",
   246  		IsVirtual:     true,
   247  		Disabled:      false,
   248  	}}
   249  	s.machineInterfaces = []net.Interface{
   250  		{Index: 1, MTU: 65535, Name: "lo", Flags: net.FlagUp | net.FlagLoopback},
   251  		{Index: 2, MTU: 1500, Name: "eth0", Flags: net.FlagUp},
   252  	}
   253  
   254  	err = lxcMachine.SetInstanceInfo("i-am-lxc", "fake_nonce", nil, s.stateNetworks, lxcInterfaces, nil, nil)
   255  	c.Assert(err, jc.ErrorIsNil)
   256  
   257  	// Login to the API as the machine agent of lxcMachine.
   258  	lxcState := s.OpenAPIAsMachine(c, lxcMachine.Tag(), password, "fake_nonce")
   259  	c.Assert(lxcState, gc.NotNil)
   260  	lxcFacade := lxcState.Networker()
   261  	c.Assert(lxcFacade, gc.NotNil)
   262  
   263  	// Create and setup networker for the LXC machine.
   264  	nw, _ := s.newCustomNetworker(c, lxcFacade, lxcMachine.Id(), true, true)
   265  	defer worker.Stop(nw)
   266  
   267  	timeout := time.After(coretesting.LongWait)
   268  	for {
   269  		select {
   270  		case cmds := <-s.lastCommands:
   271  			if !s.upInterfaces.Contains("eth0.123") {
   272  				c.Fatalf("expected command ifup eth0.123, got %v", cmds)
   273  			}
   274  			c.Assert(s.vlanModuleLoaded, jc.IsFalse)
   275  			c.Assert(nw.IsVLANModuleLoaded(), jc.IsFalse)
   276  			s.assertHaveConfig(c, nw, "", "eth0.123")
   277  			s.assertNoConfig(c, nw, "lo", "eth0")
   278  			return
   279  		case <-timeout:
   280  			c.Fatalf("no commands executed!")
   281  		}
   282  	}
   283  }
   284  
   285  type mockConfig struct {
   286  	agent.Config
   287  	tag names.Tag
   288  }
   289  
   290  func (mock *mockConfig) Tag() names.Tag {
   291  	return mock.tag
   292  }
   293  
   294  func agentConfig(machineId string) agent.Config {
   295  	return &mockConfig{tag: names.NewMachineTag(machineId)}
   296  }
   297  
   298  // Create several networks.
   299  func (s *networkerSuite) setUpNetworks(c *gc.C) {
   300  	s.stateNetworks = []state.NetworkInfo{{
   301  		Name:       "net1",
   302  		ProviderId: "net1",
   303  		CIDR:       "0.1.2.0/24",
   304  		VLANTag:    0,
   305  	}, {
   306  		Name:       "vlan42",
   307  		ProviderId: "vlan42",
   308  		CIDR:       "0.2.2.0/24",
   309  		VLANTag:    42,
   310  	}, {
   311  		Name:       "vlan69",
   312  		ProviderId: "vlan69",
   313  		CIDR:       "0.3.2.0/24",
   314  		VLANTag:    69,
   315  	}, {
   316  		Name:       "vlan123",
   317  		ProviderId: "vlan123",
   318  		CIDR:       "0.4.2.0/24",
   319  		VLANTag:    123,
   320  	}, {
   321  		Name:       "net2",
   322  		ProviderId: "net2",
   323  		CIDR:       "0.5.2.0/24",
   324  		VLANTag:    0,
   325  	}}
   326  }
   327  
   328  func (s *networkerSuite) setUpMachine(c *gc.C) {
   329  	var err error
   330  	s.stateMachine, err = s.State.AddMachine("quantal", state.JobHostUnits)
   331  	c.Assert(err, jc.ErrorIsNil)
   332  	password, err := utils.RandomPassword()
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	err = s.stateMachine.SetPassword(password)
   335  	c.Assert(err, jc.ErrorIsNil)
   336  	s.stateInterfaces = []state.NetworkInterfaceInfo{{
   337  		MACAddress:    "aa:bb:cc:dd:ee:f0",
   338  		InterfaceName: "eth0",
   339  		NetworkName:   "net1",
   340  		IsVirtual:     false,
   341  	}, {
   342  		MACAddress:    "aa:bb:cc:dd:ee:f1",
   343  		InterfaceName: "eth1",
   344  		NetworkName:   "net1",
   345  		IsVirtual:     false,
   346  	}, {
   347  		MACAddress:    "aa:bb:cc:dd:ee:f1",
   348  		InterfaceName: "eth1.42",
   349  		NetworkName:   "vlan42",
   350  		IsVirtual:     true,
   351  	}, {
   352  		MACAddress:    "aa:bb:cc:dd:ee:f0",
   353  		InterfaceName: "eth0.69",
   354  		NetworkName:   "vlan69",
   355  		IsVirtual:     true,
   356  	}, {
   357  		MACAddress:    "aa:bb:cc:dd:ee:f2",
   358  		InterfaceName: "eth2",
   359  		NetworkName:   "net2",
   360  		IsVirtual:     false,
   361  	}}
   362  	err = s.stateMachine.SetInstanceInfo("i-am", "fake_nonce", nil, s.stateNetworks, s.stateInterfaces, nil, nil)
   363  	c.Assert(err, jc.ErrorIsNil)
   364  	s.apiState = s.OpenAPIAsMachine(c, s.stateMachine.Tag(), password, "fake_nonce")
   365  	c.Assert(s.apiState, gc.NotNil)
   366  }
   367  
   368  func (s *networkerSuite) executeCommandsHook(c *gc.C, commands []string) error {
   369  	markUp := func(name string, isUp bool) {
   370  		for i, iface := range s.machineInterfaces {
   371  			if iface.Name == name {
   372  				if isUp {
   373  					iface.Flags |= net.FlagUp
   374  				} else {
   375  					iface.Flags &= ^net.FlagUp
   376  				}
   377  				s.machineInterfaces[i] = iface
   378  				return
   379  			}
   380  		}
   381  	}
   382  	for _, cmd := range commands {
   383  		args := strings.Split(cmd, " ")
   384  		if len(args) >= 2 {
   385  			what, name := args[0], args[1]
   386  			switch what {
   387  			case "ifup":
   388  				s.upInterfaces.Add(name)
   389  				s.interfacesWithAddress.Add(name)
   390  				markUp(name, true)
   391  				c.Logf("bringing %q up", name)
   392  			case "ifdown":
   393  				s.upInterfaces.Remove(name)
   394  				s.interfacesWithAddress.Remove(name)
   395  				markUp(name, false)
   396  				c.Logf("bringing %q down", name)
   397  			}
   398  		}
   399  		if strings.Contains(cmd, "modprobe 8021q") {
   400  			s.vlanModuleLoaded = true
   401  			c.Logf("VLAN module loaded")
   402  		}
   403  	}
   404  	// Send the commands without blocking.
   405  	select {
   406  	case s.lastCommands <- commands:
   407  	default:
   408  	}
   409  	return nil
   410  }
   411  
   412  func (s *networkerSuite) newCustomNetworker(
   413  	c *gc.C,
   414  	facade apinetworker.State,
   415  	machineId string,
   416  	intrusiveMode bool,
   417  	initInterfaces bool,
   418  ) (*networker.Networker, string) {
   419  	if initInterfaces {
   420  		s.upInterfaces = set.NewStrings("lo", "eth0")
   421  		s.interfacesWithAddress = set.NewStrings("lo", "eth0")
   422  	}
   423  	s.lastCommands = make(chan []string)
   424  	s.vlanModuleLoaded = false
   425  	configDir := c.MkDir()
   426  
   427  	nw, err := networker.NewNetworker(facade, agentConfig(machineId), intrusiveMode, configDir)
   428  	c.Assert(err, jc.ErrorIsNil)
   429  	c.Assert(nw, gc.NotNil)
   430  
   431  	return nw, configDir
   432  }
   433  
   434  func (s *networkerSuite) newNetworker(c *gc.C, canWriteConfig bool) *networker.Networker {
   435  	nw, _ := s.newCustomNetworker(c, s.apiFacade, s.stateMachine.Id(), canWriteConfig, true)
   436  	return nw
   437  }
   438  
   439  func (s *networkerSuite) assertNoConfig(c *gc.C, nw *networker.Networker, interfaceNames ...string) {
   440  	for _, name := range interfaceNames {
   441  		fullPath := nw.ConfigFile(name)
   442  		_, err := os.Stat(fullPath)
   443  		c.Assert(err, jc.Satisfies, os.IsNotExist)
   444  	}
   445  }
   446  
   447  func (s *networkerSuite) assertHaveConfig(c *gc.C, nw *networker.Networker, interfaceNames ...string) {
   448  	for _, name := range interfaceNames {
   449  		fullPath := nw.ConfigFile(name)
   450  		_, err := os.Stat(fullPath)
   451  		c.Assert(err, jc.ErrorIsNil)
   452  	}
   453  }