github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/networkingcommon/types.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package networkingcommon 5 6 import ( 7 "net" 8 9 "github.com/juju/errors" 10 "github.com/juju/utils/set" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/environs" 15 "github.com/juju/juju/network" 16 providercommon "github.com/juju/juju/provider/common" 17 "github.com/juju/juju/state" 18 ) 19 20 // BackingSubnet defines the methods supported by a Subnet entity 21 // stored persistently. 22 // 23 // TODO(dimitern): Once the state backing is implemented, remove this 24 // and just use *state.Subnet. 25 type BackingSubnet interface { 26 CIDR() string 27 VLANTag() int 28 ProviderId() network.Id 29 AvailabilityZones() []string 30 Status() string 31 SpaceName() string 32 Life() params.Life 33 } 34 35 // BackingSubnetInfo describes a single subnet to be added in the 36 // backing store. 37 // 38 // TODO(dimitern): Replace state.SubnetInfo with this and remove 39 // BackingSubnetInfo, once the rest of state backing methods and the 40 // following pre-reqs are done: 41 // * subnetDoc.AvailabilityZone becomes subnetDoc.AvailabilityZones, 42 // adding an upgrade step to migrate existing non empty zones on 43 // subnet docs. Also change state.Subnet.AvailabilityZone to 44 // * add subnetDoc.SpaceName - no upgrade step needed, as it will only 45 // be used for new space-aware subnets. 46 // * Subnets need a reference count to calculate Status. 47 // * ensure EC2 and MAAS providers accept empty IDs as Subnets() args 48 // and return all subnets, including the AvailabilityZones (for EC2; 49 // empty for MAAS as zones are orthogonal to networks). 50 type BackingSubnetInfo struct { 51 // ProviderId is a provider-specific network id. This may be empty. 52 ProviderId network.Id 53 54 // CIDR of the network, in 123.45.67.89/24 format. 55 CIDR string 56 57 // VLANTag needs to be between 1 and 4094 for VLANs and 0 for normal 58 // networks. It's defined by IEEE 802.1Q standard. 59 VLANTag int 60 61 // AvailabilityZones describes which availability zone(s) this 62 // subnet is in. It can be empty if the provider does not support 63 // availability zones. 64 AvailabilityZones []string 65 66 // SpaceName holds the juju network space this subnet is 67 // associated with. Can be empty if not supported. 68 SpaceName string 69 70 // Status holds the status of the subnet. Normally this will be 71 // calculated from the reference count and Life of a subnet. 72 Status string 73 74 // Live holds the life of the subnet 75 Life params.Life 76 } 77 78 // BackingSpace defines the methods supported by a Space entity stored 79 // persistently. 80 type BackingSpace interface { 81 // Name returns the space name. 82 Name() string 83 84 // Subnets returns the subnets in the space 85 Subnets() ([]BackingSubnet, error) 86 87 // ProviderId returns the network ID of the provider 88 ProviderId() network.Id 89 90 // Zones returns a list of availability zone(s) that this 91 // space is in. It can be empty if the provider does not support 92 // availability zones. 93 Zones() []string 94 95 // Life returns the lifecycle state of the space 96 Life() params.Life 97 } 98 99 // Backing defines the methods needed by the API facade to store and 100 // retrieve information from the underlying persistency layer (state 101 // DB). 102 type NetworkBacking interface { 103 environs.EnvironConfigGetter 104 105 // AvailabilityZones returns all cached availability zones (i.e. 106 // not from the provider, but in state). 107 AvailabilityZones() ([]providercommon.AvailabilityZone, error) 108 109 // SetAvailabilityZones replaces the cached list of availability 110 // zones with the given zones. 111 SetAvailabilityZones([]providercommon.AvailabilityZone) error 112 113 // AddSpace creates a space 114 AddSpace(Name string, ProviderId network.Id, Subnets []string, Public bool) error 115 116 // AllSpaces returns all known Juju network spaces. 117 AllSpaces() ([]BackingSpace, error) 118 119 // AddSubnet creates a backing subnet for an existing subnet. 120 AddSubnet(BackingSubnetInfo) (BackingSubnet, error) 121 122 // AllSubnets returns all backing subnets. 123 AllSubnets() ([]BackingSubnet, error) 124 125 // ModelTag returns the tag of the model this state is associated to. 126 ModelTag() names.ModelTag 127 } 128 129 func BackingSubnetToParamsSubnet(subnet BackingSubnet) params.Subnet { 130 cidr := subnet.CIDR() 131 vlantag := subnet.VLANTag() 132 providerid := subnet.ProviderId() 133 zones := subnet.AvailabilityZones() 134 status := subnet.Status() 135 var spaceTag names.SpaceTag 136 if subnet.SpaceName() != "" { 137 spaceTag = names.NewSpaceTag(subnet.SpaceName()) 138 } 139 140 return params.Subnet{ 141 CIDR: cidr, 142 VLANTag: vlantag, 143 ProviderId: string(providerid), 144 Zones: zones, 145 Status: status, 146 SpaceTag: spaceTag.String(), 147 Life: subnet.Life(), 148 } 149 } 150 151 // NetworkConfigFromInterfaceInfo converts a slice of network.InterfaceInfo into 152 // the equivalent params.NetworkConfig slice. 153 func NetworkConfigFromInterfaceInfo(interfaceInfos []network.InterfaceInfo) []params.NetworkConfig { 154 result := make([]params.NetworkConfig, len(interfaceInfos)) 155 for i, v := range interfaceInfos { 156 var dnsServers []string 157 for _, nameserver := range v.DNSServers { 158 dnsServers = append(dnsServers, nameserver.Value) 159 } 160 result[i] = params.NetworkConfig{ 161 DeviceIndex: v.DeviceIndex, 162 MACAddress: v.MACAddress, 163 CIDR: v.CIDR, 164 MTU: v.MTU, 165 ProviderId: string(v.ProviderId), 166 ProviderSubnetId: string(v.ProviderSubnetId), 167 ProviderSpaceId: string(v.ProviderSpaceId), 168 ProviderVLANId: string(v.ProviderVLANId), 169 ProviderAddressId: string(v.ProviderAddressId), 170 VLANTag: v.VLANTag, 171 InterfaceName: v.InterfaceName, 172 ParentInterfaceName: v.ParentInterfaceName, 173 InterfaceType: string(v.InterfaceType), 174 Disabled: v.Disabled, 175 NoAutoStart: v.NoAutoStart, 176 ConfigType: string(v.ConfigType), 177 Address: v.Address.Value, 178 DNSServers: dnsServers, 179 DNSSearchDomains: v.DNSSearchDomains, 180 GatewayAddress: v.GatewayAddress.Value, 181 } 182 } 183 return result 184 } 185 186 // NetworkConfigsToStateArgs splits the given networkConfig into a slice of 187 // state.LinkLayerDeviceArgs and a slice of state.LinkLayerDeviceAddress. The 188 // input is expected to come from MergeProviderAndObservedNetworkConfigs and to 189 // be sorted. 190 func NetworkConfigsToStateArgs(networkConfig []params.NetworkConfig) ( 191 []state.LinkLayerDeviceArgs, 192 []state.LinkLayerDeviceAddress, 193 ) { 194 var devicesArgs []state.LinkLayerDeviceArgs 195 var devicesAddrs []state.LinkLayerDeviceAddress 196 197 logger.Tracef("transforming network config to state args: %+v", networkConfig) 198 seenDeviceNames := set.NewStrings() 199 for _, netConfig := range networkConfig { 200 logger.Tracef("transforming device %q", netConfig.InterfaceName) 201 if !seenDeviceNames.Contains(netConfig.InterfaceName) { 202 // First time we see this, add it to devicesArgs. 203 seenDeviceNames.Add(netConfig.InterfaceName) 204 var mtu uint 205 if netConfig.MTU >= 0 { 206 mtu = uint(netConfig.MTU) 207 } 208 args := state.LinkLayerDeviceArgs{ 209 Name: netConfig.InterfaceName, 210 MTU: mtu, 211 ProviderID: network.Id(netConfig.ProviderId), 212 Type: state.LinkLayerDeviceType(netConfig.InterfaceType), 213 MACAddress: netConfig.MACAddress, 214 IsAutoStart: !netConfig.NoAutoStart, 215 IsUp: !netConfig.Disabled, 216 ParentName: netConfig.ParentInterfaceName, 217 } 218 logger.Tracef("state device args for device: %+v", args) 219 devicesArgs = append(devicesArgs, args) 220 } 221 222 if netConfig.CIDR == "" || netConfig.Address == "" { 223 logger.Tracef( 224 "skipping empty CIDR %q and/or Address %q of %q", 225 netConfig.CIDR, netConfig.Address, netConfig.InterfaceName, 226 ) 227 continue 228 } 229 _, ipNet, err := net.ParseCIDR(netConfig.CIDR) 230 if err != nil { 231 logger.Warningf("FIXME: ignoring unexpected CIDR format %q: %v", netConfig.CIDR, err) 232 continue 233 } 234 ipAddr := net.ParseIP(netConfig.Address) 235 if ipAddr == nil { 236 logger.Warningf("FIXME: ignoring unexpected Address format %q", netConfig.Address) 237 continue 238 } 239 ipNet.IP = ipAddr 240 cidrAddress := ipNet.String() 241 242 var derivedConfigMethod state.AddressConfigMethod 243 switch method := state.AddressConfigMethod(netConfig.ConfigType); method { 244 case state.StaticAddress, state.DynamicAddress, 245 state.LoopbackAddress, state.ManualAddress: 246 derivedConfigMethod = method 247 case "dhcp": // awkward special case 248 derivedConfigMethod = state.DynamicAddress 249 default: 250 derivedConfigMethod = state.StaticAddress 251 } 252 253 addr := state.LinkLayerDeviceAddress{ 254 DeviceName: netConfig.InterfaceName, 255 ProviderID: network.Id(netConfig.ProviderAddressId), 256 ConfigMethod: derivedConfigMethod, 257 CIDRAddress: cidrAddress, 258 DNSServers: netConfig.DNSServers, 259 DNSSearchDomains: netConfig.DNSSearchDomains, 260 GatewayAddress: netConfig.GatewayAddress, 261 } 262 logger.Tracef("state address args for device: %+v", addr) 263 devicesAddrs = append(devicesAddrs, addr) 264 } 265 logger.Tracef("seen devices: %+v", seenDeviceNames.SortedValues()) 266 logger.Tracef("network config transformed to state args:\n%+v\n%+v", devicesArgs, devicesAddrs) 267 return devicesArgs, devicesAddrs 268 } 269 270 // NetworkingEnvironFromModelConfig constructs and returns 271 // environs.NetworkingEnviron using the given configGetter. Returns an error 272 // satisfying errors.IsNotSupported() if the model config does not support 273 // networking features. 274 func NetworkingEnvironFromModelConfig(configGetter environs.EnvironConfigGetter) (environs.NetworkingEnviron, error) { 275 modelConfig, err := configGetter.ModelConfig() 276 if err != nil { 277 return nil, errors.Annotate(err, "failed to get model config") 278 } 279 if modelConfig.Type() == "dummy" { 280 return nil, errors.NotSupportedf("dummy provider network config") 281 } 282 env, err := environs.GetEnviron(configGetter, environs.New) 283 if err != nil { 284 return nil, errors.Annotate(err, "failed to construct a model from config") 285 } 286 netEnviron, supported := environs.SupportsNetworking(env) 287 if !supported { 288 // " not supported" will be appended to the message below. 289 return nil, errors.NotSupportedf("model %q networking", modelConfig.Name()) 290 } 291 return netEnviron, nil 292 } 293 294 // NetworkConfigSource defines the necessary calls to obtain the network 295 // configuration of a machine. 296 type NetworkConfigSource interface { 297 // SysClassNetPath returns the Linux kernel userspace SYSFS path used by 298 // this source. DefaultNetworkConfigSource() uses network.SysClassNetPath. 299 SysClassNetPath() string 300 301 // Interfaces returns information about all network interfaces on the 302 // machine as []net.Interface. 303 Interfaces() ([]net.Interface, error) 304 305 // InterfaceAddresses returns information about all addresses assigned to 306 // the network interface with the given name. 307 InterfaceAddresses(name string) ([]net.Addr, error) 308 } 309 310 type netPackageConfigSource struct{} 311 312 // SysClassNetPath implements NetworkConfigSource. 313 func (n *netPackageConfigSource) SysClassNetPath() string { 314 return network.SysClassNetPath 315 } 316 317 // Interfaces implements NetworkConfigSource. 318 func (n *netPackageConfigSource) Interfaces() ([]net.Interface, error) { 319 return net.Interfaces() 320 } 321 322 // InterfaceAddresses implements NetworkConfigSource. 323 func (n *netPackageConfigSource) InterfaceAddresses(name string) ([]net.Addr, error) { 324 iface, err := net.InterfaceByName(name) 325 if err != nil { 326 return nil, errors.Trace(err) 327 } 328 return iface.Addrs() 329 } 330 331 // DefaultNetworkConfigSource returns a NetworkConfigSource backed by the net 332 // package, to be used with GetObservedNetworkConfig(). 333 func DefaultNetworkConfigSource() NetworkConfigSource { 334 return &netPackageConfigSource{} 335 } 336 337 // GetObservedNetworkConfig uses the given source to find all available network 338 // interfaces and their assigned addresses, and returns the result as 339 // []params.NetworkConfig. In addition to what the source returns, a few 340 // additional transformations are done: 341 // 342 // * On any OS, the state (UP/DOWN) of each interface and the DeviceIndex field, 343 // will be correctly populated. Loopback interfaces are also properly detected 344 // and will have InterfaceType set LoopbackInterface. 345 // * On Linux only, the InterfaceType field will be reliably detected for a few 346 // types: BondInterface, BridgeInterface, VLAN_8021QInterface. 347 // * Also on Linux, for interfaces that are discovered to be ports on a bridge, 348 // the ParentInterfaceName will be populated with the name of the bridge. 349 // * ConfigType fields will be set to ConfigManual when no address is detected, 350 // or ConfigStatic when it is. 351 // * TODO: any IPv6 addresses found will be ignored and treated as empty ATM. 352 // 353 // Result entries will be grouped by InterfaceName, in the same order they are 354 // returned by the given source. 355 func GetObservedNetworkConfig(source NetworkConfigSource) ([]params.NetworkConfig, error) { 356 logger.Tracef("discovering observed machine network config...") 357 358 interfaces, err := source.Interfaces() 359 if err != nil { 360 return nil, errors.Annotate(err, "cannot get network interfaces") 361 } 362 363 var namesOrder []string 364 nameToConfigs := make(map[string][]params.NetworkConfig) 365 sysClassNetPath := source.SysClassNetPath() 366 for _, nic := range interfaces { 367 nicType := network.ParseInterfaceType(sysClassNetPath, nic.Name) 368 nicConfig := interfaceToNetworkConfig(nic, nicType) 369 370 if nicType == network.BridgeInterface { 371 updateParentForBridgePorts(nic.Name, sysClassNetPath, nameToConfigs) 372 } 373 374 seenSoFar := false 375 if existing, ok := nameToConfigs[nic.Name]; ok { 376 nicConfig.ParentInterfaceName = existing[0].ParentInterfaceName 377 // If only ParentInterfaceName was set in a previous iteration (e.g. 378 // if the bridge appeared before the port), treat the interface as 379 // not yet seen. 380 seenSoFar = existing[0].InterfaceName != "" 381 } 382 383 if !seenSoFar { 384 nameToConfigs[nic.Name] = []params.NetworkConfig(nil) 385 namesOrder = append(namesOrder, nic.Name) 386 } 387 388 addrs, err := source.InterfaceAddresses(nic.Name) 389 if err != nil { 390 return nil, errors.Annotatef(err, "cannot get interface %q addresses", nic.Name) 391 } 392 393 if len(addrs) == 0 { 394 logger.Infof("no addresses observed on interface %q", nic.Name) 395 nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfig) 396 continue 397 } 398 399 for _, addr := range addrs { 400 addressConfig, err := interfaceAddressToNetworkConfig(nic.Name, nicConfig.ConfigType, addr) 401 if err != nil { 402 return nil, errors.Trace(err) 403 } 404 405 // Need to copy nicConfig so only the fields relevant for the 406 // current address are updated. 407 nicConfigCopy := nicConfig 408 nicConfigCopy.Address = addressConfig.Address 409 nicConfigCopy.CIDR = addressConfig.CIDR 410 nicConfigCopy.ConfigType = addressConfig.ConfigType 411 nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfigCopy) 412 } 413 } 414 415 // Return all interfaces configs in input order. 416 var observedConfig []params.NetworkConfig 417 for _, name := range namesOrder { 418 observedConfig = append(observedConfig, nameToConfigs[name]...) 419 } 420 logger.Tracef("observed network config: %+v", observedConfig) 421 return observedConfig, nil 422 } 423 424 func interfaceToNetworkConfig(nic net.Interface, nicType network.InterfaceType) params.NetworkConfig { 425 configType := network.ConfigManual // assume manual initially, until we parse the address. 426 isUp := nic.Flags&net.FlagUp > 0 427 isLoopback := nic.Flags&net.FlagLoopback > 0 428 isUnknown := nicType == network.UnknownInterface 429 430 switch { 431 case isUnknown && isLoopback: 432 nicType = network.LoopbackInterface 433 configType = network.ConfigLoopback 434 case isUnknown: 435 nicType = network.EthernetInterface 436 } 437 438 return params.NetworkConfig{ 439 DeviceIndex: nic.Index, 440 MACAddress: nic.HardwareAddr.String(), 441 ConfigType: string(configType), 442 MTU: nic.MTU, 443 InterfaceName: nic.Name, 444 InterfaceType: string(nicType), 445 NoAutoStart: !isUp, 446 Disabled: !isUp, 447 } 448 } 449 450 func updateParentForBridgePorts(bridgeName, sysClassNetPath string, nameToConfigs map[string][]params.NetworkConfig) { 451 ports := network.GetBridgePorts(sysClassNetPath, bridgeName) 452 for _, portName := range ports { 453 portConfigs, ok := nameToConfigs[portName] 454 if ok { 455 portConfigs[0].ParentInterfaceName = bridgeName 456 } else { 457 portConfigs = []params.NetworkConfig{{ParentInterfaceName: bridgeName}} 458 } 459 nameToConfigs[portName] = portConfigs 460 } 461 } 462 463 func interfaceAddressToNetworkConfig(interfaceName, configType string, address net.Addr) (params.NetworkConfig, error) { 464 config := params.NetworkConfig{ 465 ConfigType: configType, 466 } 467 468 cidrAddress := address.String() 469 if cidrAddress == "" { 470 return config, nil 471 } 472 473 ip, ipNet, err := net.ParseCIDR(cidrAddress) 474 if err != nil { 475 logger.Infof("cannot parse %q on interface %q as CIDR, trying as IP address: %v", cidrAddress, interfaceName, err) 476 if ip = net.ParseIP(cidrAddress); ip == nil { 477 return config, errors.Errorf("cannot parse IP address %q on interface %q", cidrAddress, interfaceName) 478 } else { 479 ipNet = &net.IPNet{IP: ip} 480 } 481 } 482 if ip.To4() == nil { 483 logger.Debugf("skipping observed IPv6 address %q on %q: not fully supported yet", ip, interfaceName) 484 // TODO(dimitern): Treat IPv6 addresses as empty until we can handle 485 // them reliably. 486 return config, nil 487 } 488 489 if ipNet.Mask != nil { 490 config.CIDR = ipNet.String() 491 } 492 config.Address = ip.String() 493 if configType != string(network.ConfigLoopback) { 494 config.ConfigType = string(network.ConfigStatic) 495 } 496 497 // TODO(dimitern): Add DNS servers, search domains, and gateway 498 // later. 499 500 return config, nil 501 } 502 503 // MergeProviderAndObservedNetworkConfigs returns the effective network configs, 504 // using observedConfigs as a base and selectively updating it using the 505 // matching providerConfigs for each interface. 506 func MergeProviderAndObservedNetworkConfigs(providerConfigs, observedConfigs []params.NetworkConfig) []params.NetworkConfig { 507 508 providerConfigByName := networkConfigsByName(providerConfigs) 509 logger.Tracef("known provider config by name: %+v", providerConfigByName) 510 511 providerConfigByAddress := networkConfigsByAddress(providerConfigs) 512 logger.Tracef("known provider config by address: %+v", providerConfigByAddress) 513 514 var results []params.NetworkConfig 515 for _, observed := range observedConfigs { 516 517 name, ipAddress := observed.InterfaceName, observed.Address 518 finalConfig := observed 519 520 providerConfig, known := providerConfigByName[name] 521 if known { 522 finalConfig = mergeObservedAndProviderInterfaceConfig(finalConfig, providerConfig) 523 logger.Debugf("updated observed interface config for %q with: %+v", name, providerConfig) 524 } 525 526 providerConfig, known = providerConfigByAddress[ipAddress] 527 if known { 528 finalConfig = mergeObservedAndProviderAddressConfig(finalConfig, providerConfig) 529 logger.Debugf("updated observed address config for %q with: %+v", name, providerConfig) 530 } 531 532 results = append(results, finalConfig) 533 logger.Debugf("merged config for %q: %+v", name, finalConfig) 534 } 535 536 return results 537 } 538 539 func networkConfigsByName(input []params.NetworkConfig) map[string]params.NetworkConfig { 540 configsByName := make(map[string]params.NetworkConfig, len(input)) 541 for _, config := range input { 542 configsByName[config.InterfaceName] = config 543 } 544 return configsByName 545 } 546 547 func networkConfigsByAddress(input []params.NetworkConfig) map[string]params.NetworkConfig { 548 configsByAddress := make(map[string]params.NetworkConfig, len(input)) 549 for _, config := range input { 550 configsByAddress[config.Address] = config 551 } 552 return configsByAddress 553 } 554 555 func mergeObservedAndProviderInterfaceConfig(observedConfig, providerConfig params.NetworkConfig) params.NetworkConfig { 556 finalConfig := observedConfig 557 558 // The following fields cannot be observed and are only known by the 559 // provider. 560 finalConfig.ProviderId = providerConfig.ProviderId 561 finalConfig.ProviderVLANId = providerConfig.ProviderVLANId 562 finalConfig.ProviderSubnetId = providerConfig.ProviderSubnetId 563 564 // The following few fields are only updated if their observed values are 565 // empty. 566 567 if observedConfig.InterfaceType == "" { 568 finalConfig.InterfaceType = providerConfig.InterfaceType 569 } 570 571 if observedConfig.VLANTag == 0 { 572 finalConfig.VLANTag = providerConfig.VLANTag 573 } 574 575 if observedConfig.ParentInterfaceName == "" { 576 finalConfig.ParentInterfaceName = providerConfig.ParentInterfaceName 577 } 578 579 return finalConfig 580 } 581 582 func mergeObservedAndProviderAddressConfig(observedConfig, providerConfig params.NetworkConfig) params.NetworkConfig { 583 finalConfig := observedConfig 584 585 // The following fields cannot be observed and are only known by the 586 // provider. 587 finalConfig.ProviderAddressId = providerConfig.ProviderAddressId 588 finalConfig.ProviderSubnetId = providerConfig.ProviderSubnetId 589 finalConfig.ProviderSpaceId = providerConfig.ProviderSpaceId 590 591 // The following few fields are only updated if their observed values are 592 // empty. 593 594 if observedConfig.ProviderVLANId == "" { 595 finalConfig.ProviderVLANId = providerConfig.ProviderVLANId 596 } 597 598 if observedConfig.VLANTag == 0 { 599 finalConfig.VLANTag = providerConfig.VLANTag 600 } 601 602 if observedConfig.ConfigType == "" { 603 finalConfig.ConfigType = providerConfig.ConfigType 604 } 605 606 if observedConfig.CIDR == "" { 607 finalConfig.CIDR = providerConfig.CIDR 608 } 609 610 if observedConfig.GatewayAddress == "" { 611 finalConfig.GatewayAddress = providerConfig.GatewayAddress 612 } 613 614 if len(observedConfig.DNSServers) == 0 { 615 finalConfig.DNSServers = providerConfig.DNSServers 616 } 617 618 if len(observedConfig.DNSSearchDomains) == 0 { 619 finalConfig.DNSSearchDomains = providerConfig.DNSSearchDomains 620 } 621 622 return finalConfig 623 }