// IPAM takes keyword with an IP address then calls the subcommands. func Show(args []string) { doc := constants.DatastoreIntro + `Usage: calicoctl ipam show --ip=<IP> [--config=<CONFIG>] Options: -h --help Show this screen. --ip=<IP> IP address to show. -c --config=<CONFIG> Path to the file containing connection configuration in YAML or JSON format. [default: /etc/calico/calicoctl.cfg] Description: The ipam show command prints information about a given IP address, such as special attributes defined for the IP or whether the IP has been reserved by a user of the Calico IP Address Manager. ` parsedArgs, err := docopt.Parse(doc, args, true, "", false, false) if err != nil { fmt.Printf("Invalid option: 'calicoctl %s'. Use flag '--help' to read about a specific subcommand.\n", strings.Join(args, " ")) os.Exit(1) } if len(parsedArgs) == 0 { return } // Create a new backend client from env vars. cf := parsedArgs["--config"].(string) client, err := clientmgr.NewClient(cf) if err != nil { fmt.Println(err) os.Exit(1) } ipamClient := client.IPAM() passedIP := parsedArgs["--ip"].(string) ip := argutils.ValidateIP(passedIP) attr, err := ipamClient.GetAssignmentAttributes(ip) // IP address is not assigned, this prints message like // `IP 192.168.71.1 is not assigned in block`. This is not exactly an error, // so not returning it to the caller. if err != nil { fmt.Println(err) return } // IP address is assigned with attributes. if len(attr) != 0 { fmt.Println(attr) } else { // IP address is assigned but attributes are not set. fmt.Printf("No attributes defined for IP %s\n", ip) } }
func Config(args []string) { doc := constants.DatastoreIntro + `Usage: calicoctl config set <NAME> <VALUE> [--node=<NODE>] [--raw=(bgp|felix)] [--config=<CONFIG>] calicoctl config unset <NAME> [--node=<NODE>] [--raw=(bgp|felix)] [--config=<CONFIG>] calicoctl config get <NAME> [--node=<NODE>] [--raw=(bgp|felix)] [--config=<CONFIG>] Examples: # Turn off the full BGP node-to-node mesh calicoctl config set nodeToNodeMesh off # Set global log level to warning calicoctl config set logLevel warning # Set log level to info for node "node1" calicoctl config set logLevel info --node=node1 # Display the current setting for the nodeToNodeMesh calicoctl config get nodeToNodeMesh Options: -n --node=<NODE> The node name. --raw=(bgp|felix) Apply raw configuration for the specified component. This option should be used with care; the data is not validated and it is possible to configure or remove data that may prevent the component from working as expected. -c --config=<CONFIG> Path to the file containing connection configuration in YAML or JSON format. [default: /etc/calico/calicoctl.cfg] Description: These commands can be used to manage global system-wide configuration and some node-specific low level configuration. The --node option is used to specify the node name for low-level configuration that is specific to a particular node. For configuration that has both global values and node-specific values, the --node parameter is optional: including the parameter will manage the node-specific value, excluding it will manage the global value. For these options, if the node-specific value is unset, the global value will be used on the node. For configuration that is only global, the --node option should not be included. Unsetting the global value will return it to it's original default. For configuration that is node-specific only, the --node option should be included. Unsetting the node value will remove the configuration, and for supported configuration will then inherit the value from the global settings. The table below details the valid config options. Name | Scope | Value | -----------------+-------------+----------------------------------------+ logLevel | global,node | none,debug,info,warning,error,critical | nodeToNodeMesh | global | on,off | asNumber | global | 0-4294967295 | ipip | global | on,off | ` parsedArgs, err := docopt.Parse(doc, args, true, "calicoctl", false, false) if err != nil { fmt.Printf("Invalid option: 'calicoctl %s'. Use flag '--help' to read about a specific subcommand.\n", strings.Join(args, " ")) os.Exit(1) } if len(parsedArgs) == 0 { return } // Load the client config and connect. cf := parsedArgs["--config"].(string) client, err := clientmgr.NewClient(cf) if err != nil { fmt.Println(err) os.Exit(1) } // From the command line arguments construct the Config object to send to the client. node := argutils.ArgStringOrBlank(parsedArgs, "--node") raw := argutils.ArgStringOrBlank(parsedArgs, "--raw") name := argutils.ArgStringOrBlank(parsedArgs, "<NAME>") value := argutils.ArgStringOrBlank(parsedArgs, "<VALUE>") // For now we map each option through to separate config methods, but // eventually we'll aim to have a config style resource and this will // become more generic. var ct configType switch raw { case "felix": ct = rawFelixConfig{name: name, c: client.Config()} case "bgp": ct = rawBGPConfig{name: name, c: client.Config()} case "": switch strings.ToLower(name) { case "loglevel": ct = loglevel{client.Config()} case "nodetonodemesh": ct = nodemesh{client.Config()} case "asnumber": ct = asnum{client.Config()} case "ipip": ct = ipip{client.Config()} default: fmt.Printf("Error executing command: unrecognised config name '%s'\n", name) os.Exit(1) } default: fmt.Printf("Error executing command: unrecognised component '%s'\n", raw) os.Exit(1) } if parsedArgs["set"].(bool) { err = ct.set(value, node) } else if parsedArgs["unset"].(bool) { err = ct.unset(node) } else { err = ct.get(node) } if err != nil { fmt.Printf("Error executing command: %s\n", err) os.Exit(1) } return }
// executeConfigCommand is main function called by all of the resource management commands // in calicoctl (apply, create, replace, get and delete). This provides common function // for all these commands: // - Load resources from file (or if not specified determine the resource from // the command line options). // - Convert the loaded resources into a list of resources (easier to handle) // - Process each resource individually, fanning out to the appropriate methods on // the client interface, collate results and exit on the first error. func executeConfigCommand(args map[string]interface{}, action action) commandResults { var r interface{} var err error var resources []unversioned.Resource log.Info("Executing config command") if filename := args["--filename"]; filename != nil { // Filename is specified, load the resource from file and convert to a slice // of resources for easier handling. if r, err = resourcemgr.CreateResourcesFromFile(filename.(string)); err != nil { return commandResults{err: err, fileInvalid: true} } resources = convertToSliceOfResources(r) } else if r, err := getResourceFromArguments(args); err != nil { // Filename is not specific so extract the resource from the arguments. This // is only useful for delete and get functions - but we don't need to check that // here since the command syntax requires a filename for the other resource // management commands. return commandResults{err: err} } else { // We extracted a single resource type with identifiers from the CLI, convert to // a list for simpler handling. resources = []unversioned.Resource{r} } if len(resources) == 0 { return commandResults{err: errors.New("no resources specified")} } if log.GetLevel() >= log.DebugLevel { log.Debugf("Resources: %v", resources) d, err := yaml.Marshal(resources) if err != nil { return commandResults{err: err} } log.Debugf("Data: %s", string(d)) } // Load the client config and connect. cf := args["--config"].(string) client, err := clientmgr.NewClient(cf) if err != nil { return commandResults{err: err} } log.Infof("Client: %v", client) // Initialise the command results with the number of resources and the name of the // kind of resource (if only dealing with a single resource). var results commandResults var kind string count := make(map[string]int) for _, r := range resources { kind = r.GetTypeMetadata().Kind count[kind] = count[kind] + 1 results.numResources = results.numResources + 1 } if len(count) == 1 { results.singleKind = kind } // Now execute the command on each resource in order, exiting as soon as we hit an // error. for _, r := range resources { r, err = executeResourceAction(args, client, r, action) if err != nil { results.err = err break } results.resources = append(results.resources, r) results.numHandled = results.numHandled + 1 } return results }
// IPAM takes keyword with an IP address then calls the subcommands. func Release(args []string) { doc := constants.DatastoreIntro + `Usage: calicoctl ipam release --ip=<IP> [--config=<CONFIG>] Options: -h --help Show this screen. --ip=<IP> IP address to release. -c --config=<CONFIG> Path to the file containing connection configuration in YAML or JSON format. [default: /etc/calico/calicoctl.cfg] Description: The ipam release command releases an IP address from the Calico IP Address Manager that was been previously assigned to an endpoint. When an IP address is released, it becomes available for assignment to any endpoint. Note that this does not remove the IP from any existing endpoints that may be using it, so only use this command to clean up addresses from endpoints that were not cleanly removed from Calico. ` parsedArgs, err := docopt.Parse(doc, args, true, "", false, false) if err != nil { fmt.Printf("Invalid option: 'calicoctl %s'. Use flag '--help' to read about a specific subcommand.\n", strings.Join(args, " ")) os.Exit(1) } if len(parsedArgs) == 0 { return } // Create a new backend client from env vars. cf := parsedArgs["--config"].(string) client, err := clientmgr.NewClient(cf) if err != nil { fmt.Println(err) os.Exit(1) } ipamClient := client.IPAM() passedIP := parsedArgs["--ip"].(string) ip := argutils.ValidateIP(passedIP) ips := []net.IP{ip} // Call ReleaseIPs releases the IP and returns an empty slice as unallocatedIPs if // release was successful else it returns back the slice with the IP passed in. unallocatedIPs, err := ipamClient.ReleaseIPs(ips) if err != nil { fmt.Printf("Error: %v\n", err) os.Exit(1) } // Couldn't release the IP if the slice is not empty or IP might already be released/unassigned. // This is not exactly an error, so not returning it to the caller. if len(unallocatedIPs) != 0 { fmt.Printf("IP address %s is not assigned\n", ip) os.Exit(1) } // If unallocatedIPs slice is empty then IP was released Successfully. fmt.Printf("Successfully released IP address %s\n", ip) }