// 2020 Rackspace, 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 carve import ( "fmt" "log" "net" "os" "strconv" "strings" "github.com/tcnksm/go-input" "github.rackspace.com/gorax/clients/goctk" "github.com/jokelyo/argparse" "github.com/sirupsen/logrus" "github.rackspace.com/DCM/gomigrate/core" "github.rackspace.com/gorax/clients/dcxAutohost" "github.rackspace.com/gorax/gokeys" ) var ( argBlocks *[]string argEnv *string argPolicy *string argAcct *int argOverride *string ) // SubPlugin function will return a argparse.Command type back to the parent parser // nolint:typecheck func SubPlugin(p *argparse.Command) core.Plugin { // Create a command and argument for the ip audit cmd := p.NewCommand("carve", "Carve an IP block and allocate to an environment") argBlocks = cmd.Strings("b", "blocks", &argparse.Options{Help: "list of ip_block/cidr. E.g. 217.72.247.0/29", Nargs: "+", Required: true}) argEnv = cmd.String("e", "env", &argparse.Options{Help: "Environment Number", Required: false}) argAcct = cmd.Int("a", "account", &argparse.Options{Help: "CORE Account Number", Required: false}) argOverride = cmd.String("o", "override", &argparse.Options{Help: "Override for carve API", Required: false}) argPolicy = cmd.String("", "policy", &argparse.Options{Help: "Optionally change the block policy type", Default: "host", Required: false}) return core.Plugin{CMD: cmd, Func: pluginFunc} } type ( client struct { dcx *dcxAutohost.Client ipBlocks []string env string dc string acct int identityToken string override string } ) // pluginFunc function is executed from the gomigrate caller func pluginFunc(cfg gokeys.Settings) { // Set all the key from the arg parser c := client{ ipBlocks: *argBlocks, env: *argEnv, acct: *argAcct, override: *argOverride, } if len(c.ipBlocks) == 0 { logrus.Fatal("Must list at least one IP block with --blocks 217.72.247.0/29") } if c.env == "" && c.acct == 0 { logrus.Fatalf("At lease one of --account or --env required as an argument.") } var override bool if c.override != "" { // Type conversion of override parameter // Check if override value is true boolOverride, err := strconv.ParseBool(c.override) if err != nil { logrus.Fatalf("override is an optional argument but if used please give value as true.") } override = boolOverride if !override { logrus.Fatalf("override is optional argument but if used please give value as true.") } } if c.env != "" && !strings.HasPrefix(c.env, "ENV") { ui := &input.UI{Writer: os.Stdout, Reader: os.Stdin} for { envStr, err := ui.Ask("Enter a DCX environment number (i.e. ENV12345) ", &input.Options{ Required: false, HideOrder: true, }, ) if err != nil { if input.ErrInterrupted == err { return } logrus.Errorf("Input error: %s", err) continue } envStr = strings.TrimSpace(envStr) if envStr == "" { fmt.Printf("Input error: No environment passed in!\n") continue } if !strings.HasPrefix(envStr, "ENV") { fmt.Printf("Input error: %s is not a valid environment!\n", envStr) continue } c.env = envStr break } } // Pull an identity token from the users gokeys keychain key, err := cfg.Identity(false) if err != nil { log.Fatal(err) } c.identityToken = key.Password ctk, err := goctk.NewCTK(goctk.Token(c.identityToken)) if err != nil { logrus.Fatal(err) } // Grab the client for the DCX API c.dcx, err = dcxAutohost.New(dcxAutohost.WithToken(c.identityToken), false) if err != nil { logrus.Fatal(err) } if c.env != "" { envData, err := c.dcx.EnvironmentData(c.env) if err != nil { logrus.Fatal(err) } if len(envData.Gateways) < 1 { logrus.Fatalf("unable to find devices in environment %s", c.env) } comp, err := ctk.ComputerQueryLight([]int{envData.Gateways[0]}) if err != nil { logrus.Fatalf("unable to get CORE data for %s", envData.Gateways[0]) return } c.dc = comp.Result[0].DataCenter if c.acct == 0 { acct, err := strconv.Atoi(comp.Result[0].AccountNumber) if err != nil { logrus.Fatalf("Unable to determine account number. Please pass in --account flag to override") } c.acct = acct } } acctInfo, err := ctk.AccountDetails(c.acct) if err != nil { logrus.Fatalf("Unable to get account details for account %d", c.acct) return } for _, block := range c.ipBlocks { if _, _, err := net.ParseCIDR(block); err != nil { logrus.Errorf("unable to parse block into CIDR: %s", err) continue } spec := dcxAutohost.BlockSpec{ AccountNumber: strconv.Itoa(c.acct), Description: fmt.Sprintf("%d:%s %s", c.acct, acctInfo.Name, c.dc), CIDR: block, Environment: c.env, PolicyName: *argPolicy, } if override { spec.Override = override } if _, err := c.dcx.PublicIPBlocksCarve(spec); err != nil { logrus.Errorf("[%s] Failed carving block %s", block, err) continue } if c.env != "" { logrus.Infof("[%s] Block assigned to account %d:%s and allocated to %s", block, c.acct, acctInfo.Name, c.env) } else { logrus.Infof("[%s] Block assigned to account %d:%s.", block, c.acct, acctInfo.Name) } } }