From 837d3d3622a3007f6923fbb6cb373b301f262382 Mon Sep 17 00:00:00 2001 From: Sam Tresler Date: Fri, 10 Jul 2015 15:13:57 -0400 Subject: [PATCH] datasource: add packethost metadata --- coreos-cloudinit.go | 17 +++- datasource/metadata/packet/metadata.go | 106 ++++++++++++++++++++ network/packet.go | 133 +++++++++++++++++++++++++ 3 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 datasource/metadata/packet/metadata.go create mode 100644 network/packet.go diff --git a/coreos-cloudinit.go b/coreos-cloudinit.go index f147fb9..e29ff7e 100644 --- a/coreos-cloudinit.go +++ b/coreos-cloudinit.go @@ -29,6 +29,7 @@ import ( "github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma" "github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean" "github.com/coreos/coreos-cloudinit/datasource/metadata/ec2" + "github.com/coreos/coreos-cloudinit/datasource/metadata/packet" "github.com/coreos/coreos-cloudinit/datasource/proc_cmdline" "github.com/coreos/coreos-cloudinit/datasource/url" "github.com/coreos/coreos-cloudinit/datasource/waagent" @@ -57,6 +58,7 @@ var ( ec2MetadataService string cloudSigmaMetadataService bool digitalOceanMetadataService string + packetMetadataService string url string procCmdLine bool } @@ -78,6 +80,7 @@ func init() { flag.StringVar(&flags.sources.ec2MetadataService, "from-ec2-metadata", "", "Download EC2 data from the provided url") flag.BoolVar(&flags.sources.cloudSigmaMetadataService, "from-cloudsigma-metadata", false, "Download data from CloudSigma server context") flag.StringVar(&flags.sources.digitalOceanMetadataService, "from-digitalocean-metadata", "", "Download DigitalOcean data from the provided url") + flag.StringVar(&flags.sources.packetMetadataService, "from-packet-metadata", "", "Download Packet data from metadata service") flag.StringVar(&flags.sources.url, "from-url", "", "Download user-data from provided url") flag.BoolVar(&flags.sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=', using the cloud-config served by an HTTP GET to ", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag)) flag.StringVar(&flags.oem, "oem", "", "Use the settings specific to the provided OEM") @@ -109,6 +112,10 @@ var ( "cloudsigma": oemConfig{ "from-cloudsigma-metadata": "true", }, + "packet": oemConfig{ + "from-packet-metadata": "https://metadata.packet.net/", + "convert-netconf": "packet", + }, } ) @@ -139,14 +146,15 @@ func main() { case "": case "debian": case "digitalocean": + case "packet": default: - fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean'\n", flags.convertNetconf) + fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean, packet'\n", flags.convertNetconf) os.Exit(2) } dss := getDatasources() if len(dss) == 0 { - fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-url or --from-proc-cmdline") + fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-packet-metadata, --from-url or --from-proc-cmdline") os.Exit(2) } @@ -215,6 +223,8 @@ func main() { ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig) case "digitalocean": ifaces, err = network.ProcessDigitalOceanNetconf(metadata.NetworkConfig) + case "packet": + ifaces, err = network.ProcessPacketNetconf(metadata.NetworkConfig) default: err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf) } @@ -290,6 +300,9 @@ func getDatasources() []datasource.Datasource { if flags.sources.waagent != "" { dss = append(dss, waagent.NewDatasource(flags.sources.waagent)) } + if flags.sources.packetMetadataService != "" { + dss = append(dss, packet.NewDatasource(flags.sources.packetMetadataService)) + } if flags.sources.procCmdLine { dss = append(dss, proc_cmdline.NewDatasource()) } diff --git a/datasource/metadata/packet/metadata.go b/datasource/metadata/packet/metadata.go new file mode 100644 index 0000000..3928a2c --- /dev/null +++ b/datasource/metadata/packet/metadata.go @@ -0,0 +1,106 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package packet + +import ( + "encoding/json" + "net" + "strconv" + + "github.com/coreos/coreos-cloudinit/datasource" + "github.com/coreos/coreos-cloudinit/datasource/metadata" +) + +const ( + DefaultAddress = "https://metadata.packet.net/" + apiVersion = "" + userdataUrl = "userdata" + metadataPath = "metadata" +) + +type Netblock struct { + Address net.IP `json:"address"` + Cidr int `json:"cidr"` + Netmask net.IP `json:"netmask"` + Gateway net.IP `json:"gateway"` + AddressFamily int `json:"address_family"` + Public bool `json:"public"` +} + +type Nic struct { + Name string `json:"name"` + Mac string `json:"mac"` +} + +type NetworkData struct { + Interfaces []Nic `json:"interfaces"` + Netblocks []Netblock `json:"addresses"` + DNS []net.IP `json:"dns"` +} + +// Metadata that will be pulled from the https://metadata.packet.net/metadata only. We have the opportunity to add more later. +type Metadata struct { + Hostname string `json:"hostname"` + SSHKeys []string `json:"ssh_keys"` + NetworkData NetworkData `json:"network"` +} + +type metadataService struct { + metadata.MetadataService +} + +func NewDatasource(root string) *metadataService { + return &metadataService{MetadataService: metadata.NewDatasource(root, apiVersion, userdataUrl, metadataPath)} +} + +func (ms *metadataService) FetchMetadata() (metadata datasource.Metadata, err error) { + var data []byte + var m Metadata + + if data, err = ms.FetchData(ms.MetadataUrl()); err != nil || len(data) == 0 { + return + } + + if err = json.Unmarshal(data, &m); err != nil { + return + } + + if len(m.NetworkData.Netblocks) > 0 { + for _, Netblock := range m.NetworkData.Netblocks { + if Netblock.AddressFamily == 4 { + if Netblock.Public == true { + metadata.PublicIPv4 = Netblock.Address + } else { + metadata.PrivateIPv4 = Netblock.Address + } + } else { + metadata.PublicIPv6 = Netblock.Address + } + } + } + metadata.Hostname = m.Hostname + metadata.SSHPublicKeys = map[string]string{} + for i, key := range m.SSHKeys { + metadata.SSHPublicKeys[strconv.Itoa(i)] = key + } + + metadata.NetworkConfig, err = json.Marshal(m.NetworkData) + + return +} + +func (ms metadataService) Type() string { + return "packet-metadata-service" +} diff --git a/network/packet.go b/network/packet.go new file mode 100644 index 0000000..e763049 --- /dev/null +++ b/network/packet.go @@ -0,0 +1,133 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "encoding/json" + "net" + + "github.com/coreos/coreos-cloudinit/datasource/metadata/packet" +) + +func ProcessPacketNetconf(config []byte) ([]InterfaceGenerator, error) { + var netdata packet.NetworkData + if err := json.Unmarshal(config, &netdata); err != nil { + return nil, err + } + + var nameservers []net.IP + if netdata.DNS != nil { + nameservers = netdata.DNS + } else { + nameservers = append(nameservers, net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4")) + } + + generators, err := parseNetwork(netdata, nameservers) + if err != nil { + return nil, err + } + + return generators, nil +} + +func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]InterfaceGenerator, error) { + var interfaces []InterfaceGenerator + var addresses []net.IPNet + var routes []route + for _, netblock := range netdata.Netblocks { + addresses = append(addresses, net.IPNet{ + IP: netblock.Address, + Mask: net.IPMask(netblock.Netmask), + }) + if netblock.Public == false { + routes = append(routes, route{ + destination: net.IPNet{ + IP: net.IPv4(10, 0, 0, 0), + Mask: net.IPv4Mask(255, 0, 0, 0), + }, + gateway: netblock.Gateway, + }) + } else { + if netblock.AddressFamily == 4 { + routes = append(routes, route{ + destination: net.IPNet{ + IP: net.IPv4zero, + Mask: net.IPMask(net.IPv4zero), + }, + gateway: netblock.Gateway, + }) + } else { + routes = append(routes, route{ + destination: net.IPNet{ + IP: net.IPv6zero, + Mask: net.IPMask(net.IPv6zero), + }, + gateway: netblock.Gateway, + }) + } + } + } + + bond := bondInterface{ + logicalInterface: logicalInterface{ + name: "bond0", + config: configMethodStatic{ + addresses: addresses, + nameservers: nameservers, + routes: routes, + }, + }, + options: map[string]string{ + "Mode": "802.3ad", + "LACPTransmitRate": "fast", + "MIIMonitorSec": ".2", + "UpDelaySec": ".2", + "DownDelaySec": ".2", + }, + } + + for _, iface := range netdata.Interfaces { + if iface.Name != "chassis0" && iface.Name != "ipmi0" { + bond.slaves = append(bond.slaves, iface.Name) + if iface.Name == "enp1s0f0" { + bond.hwaddr, _ = net.ParseMAC(iface.Mac) + } + } + } + + for _, iface := range netdata.Interfaces { + if iface.Name != "chassis0" && iface.Name != "ipmi0" { + p := physicalInterface{ + logicalInterface: logicalInterface{ + name: iface.Name, + config: configMethodStatic{ + nameservers: nameservers, + }, + children: []networkInterface{&bond}, + }, + } + + if iface.Name == "enp1s0f0" { + p.configDepth = 20 + } + + interfaces = append(interfaces, &p) + } + } + + interfaces = append(interfaces, &bond) + + return interfaces, nil +}