github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/provider/maas/devices.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package maas
     5  
     6  import (
     7  	"encoding/json"
     8  	"net/url"
     9  	"path"
    10  	"strconv"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/gomaasapi"
    14  
    15  	"github.com/juju/juju/instance"
    16  	"github.com/juju/juju/network"
    17  )
    18  
    19  // TODO(dimitern): The types below should be part of gomaasapi.
    20  // LKK Card: https://canonical.leankit.com/Boards/View/101652562/119310616
    21  
    22  type maasZone struct {
    23  	Name        string `json:"name"`
    24  	Description string `json:"description"`
    25  	ResourceURI string `json:"resource_uri"`
    26  }
    27  
    28  type maasMACAddress struct {
    29  	MACAddress string `json:"mac_address"`
    30  }
    31  
    32  type maasDevice struct {
    33  	SystemID      string           `json:"system_id"`
    34  	Parent        string           `json:"parent"`
    35  	Hostname      string           `json:"hostname"`
    36  	IPAddresses   []string         `json:"ip_addresses"`
    37  	Owner         string           `json:"owner"`
    38  	Zone          maasZone         `json:"zone"`
    39  	MACAddressSet []maasMACAddress `json:"macaddress_set"`
    40  	TagNames      []string         `json:"tag_names"`
    41  	ResourceURI   string           `json:"resource_uri"`
    42  }
    43  
    44  func parseDevice(jsonBytes []byte) (*maasDevice, error) {
    45  	var device maasDevice
    46  	if err := json.Unmarshal(jsonBytes, &device); err != nil {
    47  		return nil, errors.Annotate(err, "parsing device")
    48  	}
    49  	return &device, nil
    50  }
    51  
    52  func getJSONBytes(object json.Marshaler) ([]byte, error) {
    53  	rawBytes, err := object.MarshalJSON()
    54  	if err != nil {
    55  		return nil, errors.Annotate(err, "cannot get JSON bytes")
    56  	}
    57  	return rawBytes, nil
    58  }
    59  
    60  func (env *maasEnviron) createDevice(hostInstanceID instance.Id, primaryMACAddress string) (*maasDevice, error) {
    61  	devicesAPI := env.getMAASClient().GetSubObject("devices")
    62  	params := make(url.Values)
    63  	params.Add("parent", extractSystemId(hostInstanceID))
    64  	params.Add("mac_addresses", primaryMACAddress)
    65  
    66  	result, err := devicesAPI.CallPost("new", params)
    67  	if err != nil {
    68  		return nil, errors.Trace(err)
    69  	}
    70  	deviceJSON, err := getJSONBytes(result)
    71  	if err != nil {
    72  		return nil, errors.Trace(err)
    73  	}
    74  	device, err := parseDevice(deviceJSON)
    75  	if err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  	logger.Debugf("created device: %+v", device)
    79  	return device, nil
    80  }
    81  
    82  func parseInterface(jsonBytes []byte) (*maasInterface, error) {
    83  	var iface maasInterface
    84  	if err := json.Unmarshal(jsonBytes, &iface); err != nil {
    85  		return nil, errors.Annotate(err, "parsing interface")
    86  	}
    87  	return &iface, nil
    88  }
    89  
    90  func (env *maasEnviron) createDeviceInterface(deviceID instance.Id, name, macAddress, vlanID string) (*maasInterface, error) {
    91  	deviceSystemID := extractSystemId(deviceID)
    92  	uri := path.Join("nodes", deviceSystemID, "interfaces")
    93  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
    94  
    95  	params := make(url.Values)
    96  	params.Add("name", name)
    97  	params.Add("mac_address", macAddress)
    98  	params.Add("vlan", vlanID)
    99  
   100  	result, err := interfacesAPI.CallPost("create_physical", params)
   101  	if err != nil {
   102  		return nil, errors.Trace(err)
   103  	}
   104  	interfaceJSON, err := getJSONBytes(result)
   105  	if err != nil {
   106  		return nil, errors.Trace(err)
   107  	}
   108  	iface, err := parseInterface(interfaceJSON)
   109  	if err != nil {
   110  		return nil, errors.Trace(err)
   111  	}
   112  	return iface, nil
   113  }
   114  
   115  func (env *maasEnviron) updateDeviceInterface(deviceID instance.Id, interfaceID, name, macAddress, vlanID string) (*maasInterface, error) {
   116  	deviceSystemID := extractSystemId(deviceID)
   117  	uri := path.Join("nodes", deviceSystemID, "interfaces", interfaceID)
   118  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
   119  
   120  	params := make(url.Values)
   121  	params.Add("name", name)
   122  	params.Add("mac_address", macAddress)
   123  	params.Add("vlan", vlanID)
   124  
   125  	result, err := interfacesAPI.Update(params)
   126  	if err != nil {
   127  		return nil, errors.Trace(err)
   128  	}
   129  	interfaceJSON, err := getJSONBytes(result)
   130  	if err != nil {
   131  		return nil, errors.Trace(err)
   132  	}
   133  	iface, err := parseInterface(interfaceJSON)
   134  	if err != nil {
   135  		return nil, errors.Trace(err)
   136  	}
   137  	return iface, nil
   138  }
   139  
   140  func (env *maasEnviron) linkDeviceInterfaceToSubnet(deviceID instance.Id, interfaceID, subnetID string, mode maasLinkMode) (*maasInterface, error) {
   141  	deviceSystemID := extractSystemId(deviceID)
   142  	uri := path.Join("nodes", deviceSystemID, "interfaces", interfaceID)
   143  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
   144  
   145  	params := make(url.Values)
   146  	params.Add("mode", string(mode))
   147  	params.Add("subnet", subnetID)
   148  
   149  	result, err := interfacesAPI.CallPost("link_subnet", params)
   150  	if err != nil {
   151  		return nil, errors.Trace(err)
   152  	}
   153  	interfaceJSON, err := getJSONBytes(result)
   154  	if err != nil {
   155  		return nil, errors.Trace(err)
   156  	}
   157  	iface, err := parseInterface(interfaceJSON)
   158  	if err != nil {
   159  		return nil, errors.Trace(err)
   160  	}
   161  	return iface, nil
   162  }
   163  
   164  func (env *maasEnviron) deviceInterfaces(deviceID instance.Id) ([]maasInterface, error) {
   165  	deviceSystemID := extractSystemId(deviceID)
   166  	uri := path.Join("nodes", deviceSystemID, "interfaces")
   167  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
   168  
   169  	result, err := interfacesAPI.CallGet("", nil)
   170  	if err != nil {
   171  		return nil, errors.Trace(err)
   172  	}
   173  	interfacesJSON, err := getJSONBytes(result)
   174  	if err != nil {
   175  		return nil, errors.Trace(err)
   176  	}
   177  	interfaces, err := parseInterfaces(interfacesJSON)
   178  	if err != nil {
   179  		return nil, errors.Trace(err)
   180  	}
   181  	logger.Debugf("device %q interfaces: %+v", deviceSystemID, interfaces)
   182  	return interfaces, nil
   183  
   184  }
   185  
   186  func (env *maasEnviron) deviceInterfaceInfo(deviceID instance.Id, nameToParentName map[string]string) ([]network.InterfaceInfo, error) {
   187  	interfaces, err := env.deviceInterfaces(deviceID)
   188  	if err != nil {
   189  		return nil, errors.Trace(err)
   190  	}
   191  
   192  	interfaceInfo := make([]network.InterfaceInfo, 0, len(interfaces))
   193  	for _, nic := range interfaces {
   194  		nicInfo := network.InterfaceInfo{
   195  			InterfaceName:       nic.Name,
   196  			InterfaceType:       network.EthernetInterface,
   197  			MACAddress:          nic.MACAddress,
   198  			MTU:                 nic.EffectveMTU,
   199  			VLANTag:             nic.VLAN.VID,
   200  			ProviderId:          network.Id(strconv.Itoa(nic.ID)),
   201  			ProviderVLANId:      network.Id(strconv.Itoa(nic.VLAN.ID)),
   202  			Disabled:            !nic.Enabled,
   203  			NoAutoStart:         !nic.Enabled,
   204  			ParentInterfaceName: nameToParentName[nic.Name],
   205  		}
   206  
   207  		if len(nic.Links) == 0 {
   208  			logger.Debugf("device %q interface %q has no links", deviceID, nic.Name)
   209  			interfaceInfo = append(interfaceInfo, nicInfo)
   210  			continue
   211  		}
   212  
   213  		for _, link := range nic.Links {
   214  			switch link.Mode {
   215  			case modeUnknown:
   216  				nicInfo.ConfigType = network.ConfigUnknown
   217  			case modeDHCP:
   218  				nicInfo.ConfigType = network.ConfigDHCP
   219  			case modeStatic, modeLinkUp:
   220  				nicInfo.ConfigType = network.ConfigStatic
   221  			default:
   222  				nicInfo.ConfigType = network.ConfigManual
   223  			}
   224  
   225  			if link.IPAddress == "" {
   226  				logger.Debugf("device %q interface %q has no address", deviceID, nic.Name)
   227  				continue
   228  			}
   229  			if link.Subnet == nil {
   230  				logger.Debugf("device %q interface %q link %d missing subnet", deviceID, nic.Name, link.ID)
   231  				continue
   232  			}
   233  
   234  			nicInfo.CIDR = link.Subnet.CIDR
   235  			nicInfo.Address = network.NewAddressOnSpace(link.Subnet.Space, link.IPAddress)
   236  			nicInfo.ProviderSubnetId = network.Id(strconv.Itoa(link.Subnet.ID))
   237  			nicInfo.ProviderAddressId = network.Id(strconv.Itoa(link.ID))
   238  			if link.Subnet.GatewayIP != "" {
   239  				nicInfo.GatewayAddress = network.NewAddressOnSpace(link.Subnet.Space, link.Subnet.GatewayIP)
   240  			}
   241  			if len(link.Subnet.DNSServers) > 0 {
   242  				nicInfo.DNSServers = network.NewAddressesOnSpace(link.Subnet.Space, link.Subnet.DNSServers...)
   243  			}
   244  
   245  			interfaceInfo = append(interfaceInfo, nicInfo)
   246  		}
   247  	}
   248  	logger.Debugf("device %q has interface info: %+v", deviceID, interfaceInfo)
   249  	return interfaceInfo, nil
   250  }
   251  
   252  func (env *maasEnviron) deviceInterfaceInfo2(deviceID string, nameToParentName map[string]string) ([]network.InterfaceInfo, error) {
   253  	args := gomaasapi.DevicesArgs{SystemIDs: []string{deviceID}}
   254  	devices, err := env.maasController.Devices(args)
   255  	if err != nil {
   256  		return nil, errors.Trace(err)
   257  	}
   258  	if len(devices) != 1 {
   259  		return nil, errors.Errorf("unexpected response requesting device %v: %v", deviceID, devices)
   260  	}
   261  	interfaces := devices[0].InterfaceSet()
   262  
   263  	interfaceInfo := make([]network.InterfaceInfo, 0, len(interfaces))
   264  	for _, nic := range interfaces {
   265  		vlanId := 0
   266  		vlanVid := 0
   267  		vlan := nic.VLAN()
   268  		if vlan != nil {
   269  			vlanId = vlan.ID()
   270  			vlanVid = vlan.VID()
   271  		}
   272  		nicInfo := network.InterfaceInfo{
   273  			InterfaceName:       nic.Name(),
   274  			InterfaceType:       network.EthernetInterface,
   275  			MACAddress:          nic.MACAddress(),
   276  			MTU:                 nic.EffectiveMTU(),
   277  			VLANTag:             vlanVid,
   278  			ProviderId:          network.Id(strconv.Itoa(nic.ID())),
   279  			ProviderVLANId:      network.Id(strconv.Itoa(vlanId)),
   280  			Disabled:            !nic.Enabled(),
   281  			NoAutoStart:         !nic.Enabled(),
   282  			ParentInterfaceName: nameToParentName[nic.Name()],
   283  		}
   284  
   285  		if len(nic.Links()) == 0 {
   286  			logger.Debugf("device %q interface %q has no links", deviceID, nic.Name())
   287  			interfaceInfo = append(interfaceInfo, nicInfo)
   288  			continue
   289  		}
   290  
   291  		for _, link := range nic.Links() {
   292  			mode := maasLinkMode(link.Mode())
   293  			switch mode {
   294  			case modeUnknown:
   295  				nicInfo.ConfigType = network.ConfigUnknown
   296  			case modeDHCP:
   297  				nicInfo.ConfigType = network.ConfigDHCP
   298  			case modeStatic, modeLinkUp:
   299  				nicInfo.ConfigType = network.ConfigStatic
   300  			default:
   301  				nicInfo.ConfigType = network.ConfigManual
   302  			}
   303  
   304  			subnet := link.Subnet()
   305  			if link.IPAddress() == "" || subnet == nil {
   306  				logger.Debugf("device %q interface %q has no address", deviceID, nic.Name())
   307  				continue
   308  			}
   309  
   310  			nicInfo.CIDR = subnet.CIDR()
   311  			nicInfo.Address = network.NewAddressOnSpace(subnet.Space(), link.IPAddress())
   312  			nicInfo.ProviderSubnetId = network.Id(strconv.Itoa(subnet.ID()))
   313  			nicInfo.ProviderAddressId = network.Id(strconv.Itoa(link.ID()))
   314  			if subnet.Gateway() != "" {
   315  				nicInfo.GatewayAddress = network.NewAddressOnSpace(subnet.Space(), subnet.Gateway())
   316  			}
   317  			if len(subnet.DNSServers()) > 0 {
   318  				nicInfo.DNSServers = network.NewAddressesOnSpace(subnet.Space(), subnet.DNSServers()...)
   319  			}
   320  
   321  			interfaceInfo = append(interfaceInfo, nicInfo)
   322  		}
   323  	}
   324  	logger.Debugf("device %q has interface info: %+v", deviceID, interfaceInfo)
   325  	return interfaceInfo, nil
   326  }