Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enabled VRF IP reservations #418

Merged
merged 1 commit into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/metal_ip_get.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Retrieves information about IP addresses, IP address reservations, and IP addres
Retrieves information about the IP addresses in a project, the IP addresses that are in a specified assignment, or the IP addresses that are in a specified reservation.

```
metal ip get -p <project_UUID> | -a <assignment_UUID> | -r <reservation_UUID> [flags]
metal ip get -p <project-id> | -a <assignment-id> | -r <reservation-id> [flags]
```

### Examples
Expand Down
11 changes: 9 additions & 2 deletions docs/metal_ip_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,34 @@ Request a block of IP addresses.
Requests either a block of public IPv4 addresses or global IPv4 addresses for your project in a specific metro or facility.

```
metal ip request -p <project_id> -t <ip_address_type> -q <quantity> (-m <metro> | -f <facility>) [-f <flags>] [-c <comments>] [flags]
metal ip request -p <project-id> -t <ip_address_type> -q <quantity> (-m <metro> | -f <facility>) [-f <flags>] [-c <comments>] [flags]
```

### Examples

```
# Requests a block of 4 public IPv4 addresses in Dallas:
metal ip request -p $METAL_PROJECT_ID -t public_ipv4 -q 4 -m da

metal ip request -v df18fbd8-2919-4104-a042-5d42a05b8eed -t vrf --cidr 24 -n 172.89.1.0 --tags foo --tags bar --customdata '{"my":"goodness"}' --details "i don't think VRF users need this or will see it after submitting the request"
```

### Options

```
--cidr int The size of the desired subnet in bits.
-c, --comments string General comments or description.
--customdata string customdata is to add to the reservation, in a comma-separated list.
--details string VRF IP Reservation's details
-f, --facility string Code of the facility where the IP Reservation will be created
-h, --help help for request
-m, --metro string Code of the metro where the IP Reservation will be created
-n, --network string The starting address for this VRF IP Reservation's subnet
-p, --project-id string The project's UUID. This flag is required, unless specified in the config created by metal init or set as METAL_PROJECT_ID environment variable.
-q, --quantity int Number of IP addresses to reserve.
--tags strings Tag or Tags to add to the reservation, in a comma-separated list.
--tags strings Adds the tags for the IP Reservations --tags "tag1,tag2" OR --tags "tag1" --tags "tag2"
-t, --type string The type of IP Address, either public_ipv4 or global_ipv4.
-v, --vrf-id string Specify the VRF UUID.
```

### Options inherited from parent commands
Expand Down
1 change: 1 addition & 0 deletions docs/metal_vrf.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ VRF operations : It defines a collection of customer-managed IP blocks that can
* [metal vrf create](metal_vrf_create.md) - Creates a Virtual Routing and Forwarding(VRF) for a specified project.
* [metal vrf delete](metal_vrf_delete.md) - Deletes a VRF.
* [metal vrf get](metal_vrf_get.md) - Lists VRFs.
* [metal vrf ips](metal_vrf_ips.md) - Retrieves the list of VRF IP Reservations for the VRF.

50 changes: 50 additions & 0 deletions docs/metal_vrf_ips.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## metal vrf ips

Retrieves the list of VRF IP Reservations for the VRF.

### Synopsis

Retrieves the list of VRF IP Reservations for the VRF.

```
metal vrf ips [-v <vrf-id] [-i <ip-id>] [flags]
```

### Examples

```
# Retrieves the list of VRF IP Reservations for the VRF.

metal vrf ips [-v <vrf-id]

# Retrieve a specific IP Reservation for a VRF
metal vrf ips [-v <vrf-id] [-i <ip-id>]
```

### Options

```
-h, --help help for ips
-i, --id string Specify the IP UUID to retrieve the details of a VRF IP reservation.
-v, --vrf-id string Specify the VRF UUID to list its associated IP reservations.
```

### Options inherited from parent commands

```
--config string Path to JSON or YAML configuration file (METAL_CONFIG)
--exclude strings Comma separated Href references to collapse in results, may be dotted three levels deep
--filter stringArray Filter 'get' actions with name value pairs. Filter is not supported by all resources and is implemented as request query parameters.
--http-header strings Headers to add to requests (in format key=value)
--include strings Comma separated Href references to expand in results, may be dotted three levels deep
-o, --output string Output format (*table, json, yaml). env output formats are (*sh, terraform, capp).
--search string Search keyword for use in 'get' actions. Search is not supported by all resources.
--sort-by string Sort fields for use in 'get' actions. Sort is not supported by all resources.
--sort-dir string Sort field direction for use in 'get' actions. Sort is not supported by all resources.
--token string Metal API Token (METAL_AUTH_TOKEN)
```

### SEE ALSO

* [metal vrf](metal_vrf.md) - VRF operations : create, get, delete

133 changes: 85 additions & 48 deletions internal/ips/request.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
// Copyright © 2018 Jasmin Gacic <jasmin@stackpointcloud.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package ips

import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"strconv"

metal "github.com/equinix/equinix-sdk-go/services/metalv1"
Expand All @@ -31,50 +14,100 @@ import (

func (c *Client) Request() *cobra.Command {
var (
ttype string
quantity int
comments string
facility string
metro string
projectID string
tags []string
ttype string
quantity int
comments string
facility string
metro string
projectID string
cidr int
network string
vrfID string
details string
tags []string
customdata string
)

// requestIPCmd represents the requestIp command
requestIPCmd := &cobra.Command{
Use: `request -p <project_id> -t <ip_address_type> -q <quantity> (-m <metro> | -f <facility>) [-f <flags>] [-c <comments>]`,
Use: `request -p <project-id> -t <ip_address_type> -q <quantity> (-m <metro> | -f <facility>) [-f <flags>] [-c <comments>]`,
Short: "Request a block of IP addresses.",
Long: "Requests either a block of public IPv4 addresses or global IPv4 addresses for your project in a specific metro or facility.",
Example: ` # Requests a block of 4 public IPv4 addresses in Dallas:
metal ip request -p $METAL_PROJECT_ID -t public_ipv4 -q 4 -m da`,
metal ip request -p $METAL_PROJECT_ID -t public_ipv4 -q 4 -m da
metal ip request -v df18fbd8-2919-4104-a042-5d42a05b8eed -t vrf --cidr 24 -n 172.89.1.0 --tags foo --tags bar --customdata '{"my":"goodness"}' --details "i don't think VRF users need this or will see it after submitting the request"`,

RunE: func(cmd *cobra.Command, args []string) error {
var (
req *metal.IPReservationRequestInput
vrfReq *metal.VrfIpReservationCreateInput
requestIPReservationRequest *metal.RequestIPReservationRequest
)
cmd.SilenceUsage = true
// It's a required flag in case of VRFIPReservations and we conduct thorough validation to ensure its inclusion.
// By detecting its presence, we can identify whether it pertains to a standard IP Reservation Request or a VRF IP Reservation Request.

req := &metal.IPReservationRequestInput{
Metro: &metro,
Tags: tags,
Quantity: int32(quantity),
Type: ttype,
Facility: &facility,
}
if ttype != "vrf" {

requestIPReservationRequest := &metal.RequestIPReservationRequest{
IPReservationRequestInput: req,
}
req = &metal.IPReservationRequestInput{
Metro: &metro,
Tags: tags,
Quantity: int32(quantity),
Type: ttype,
Facility: &facility,
}

requestIPReservationRequest = &metal.RequestIPReservationRequest{
IPReservationRequestInput: req,
}
} else {
// Below are required Flags in VRF IP Reservation Request.
if cidr == 0 || network == "" || vrfID == "" {
return errors.New(" cidr, network and ID of the VRF are required to create VFR IP Reservations")
}
// This is an optinal Flag in VRF IP Reservation Request.
var data map[string]interface{}
if customdata != "" {
err := json.Unmarshal([]byte(customdata), &data)
if err != nil {
log.Fatalf("Error parsing custom data: %v", err)
}
}

vrfReq = &metal.VrfIpReservationCreateInput{
Type: ttype,
Cidr: int32(cidr),
Network: network,
VrfId: vrfID,
Details: &details,
Customdata: data,
Tags: tags,
}
requestIPReservationRequest = &metal.RequestIPReservationRequest{
VrfIpReservationCreateInput: vrfReq,
}
}
reservation, _, err := c.IPService.RequestIPReservation(context.Background(), projectID).RequestIPReservationRequest(*requestIPReservationRequest).Execute()
if err != nil {
return fmt.Errorf("Could not request IP addresses: %w", err)
return fmt.Errorf("could not request IP addresses: %w", err)
}

data := make([][]string, 1)

data[0] = []string{reservation.IPReservation.GetId(),
reservation.IPReservation.GetAddress(),
strconv.FormatBool(reservation.IPReservation.GetPublic()),
reservation.IPReservation.CreatedAt.String()}
header := []string{"ID", "Address", "Public", "Created"}
if ttype != "vrf" {
data[0] = []string{reservation.IPReservation.GetId(),
string(reservation.IPReservation.GetType()),
reservation.IPReservation.GetAddress(),
strconv.FormatBool(reservation.IPReservation.GetPublic()),
reservation.IPReservation.CreatedAt.String()}
} else {
data[0] = []string{reservation.VrfIpReservation.GetId(),
string(reservation.VrfIpReservation.GetType()),
reservation.VrfIpReservation.GetAddress(),
strconv.FormatBool(reservation.VrfIpReservation.GetPublic()),
reservation.VrfIpReservation.CreatedAt.String()}
}
header := []string{"ID", "Type", "Address", "Public", "Created"}

ctreatma marked this conversation as resolved.
Show resolved Hide resolved
return c.Out.Output(reservation, header, &data)
},
Expand All @@ -85,11 +118,15 @@ func (c *Client) Request() *cobra.Command {
requestIPCmd.Flags().StringVarP(&facility, "facility", "f", "", "Code of the facility where the IP Reservation will be created")
requestIPCmd.Flags().StringVarP(&metro, "metro", "m", "", "Code of the metro where the IP Reservation will be created")
requestIPCmd.Flags().IntVarP(&quantity, "quantity", "q", 0, "Number of IP addresses to reserve.")
requestIPCmd.Flags().StringSliceVar(&tags, "tags", nil, "Tag or Tags to add to the reservation, in a comma-separated list.")
requestIPCmd.Flags().StringSliceVarP(&tags, "tags", "", []string{}, `Adds the tags for the IP Reservations --tags "tag1,tag2" OR --tags "tag1" --tags "tag2"`)
requestIPCmd.Flags().IntVar(&cidr, "cidr", 0, "The size of the desired subnet in bits.")
requestIPCmd.Flags().StringVarP(&network, "network", "n", "", "The starting address for this VRF IP Reservation's subnet")
requestIPCmd.Flags().StringVarP(&vrfID, "vrf-id", "v", "", "Specify the VRF UUID.")
requestIPCmd.Flags().StringVarP(&details, "details", "", "", "VRF IP Reservation's details")
requestIPCmd.Flags().StringVarP(&customdata, "customdata", "", "", "customdata is to add to the reservation, in a comma-separated list.")

_ = requestIPCmd.MarkFlagRequired("project-id")
_ = requestIPCmd.MarkFlagRequired("type")
_ = requestIPCmd.MarkFlagRequired("quantity")

ctreatma marked this conversation as resolved.
Show resolved Hide resolved
requestIPCmd.Flags().StringVarP(&comments, "comments", "c", "", "General comments or description.")
return requestIPCmd
Expand Down
52 changes: 32 additions & 20 deletions internal/ips/retrieve.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (c *Client) Retrieve() *cobra.Command {

// ipCmd represents the ip command
retrieveIPCmd := &cobra.Command{
Use: `get -p <project_UUID> | -a <assignment_UUID> | -r <reservation_UUID>`,
Use: `get -p <project-id> | -a <assignment-id> | -r <reservation-id>`,
Aliases: []string{"list"},
Short: "Retrieves information about IP addresses, IP address reservations, and IP address assignments.",
Long: "Retrieves information about the IP addresses in a project, the IP addresses that are in a specified assignment, or the IP addresses that are in a specified reservation.",
Expand Down Expand Up @@ -68,57 +68,69 @@ func (c *Client) Retrieve() *cobra.Command {
if assignmentID != "" {
ip, _, err := c.IPService.FindIPAddressById(context.Background(), assignmentID).Include(inc).Exclude(exc).Execute()
if err != nil {
return fmt.Errorf("Could not get Device IP address: %w", err)
return fmt.Errorf("could not get Device IP address: %w", err)
}

data := make([][]string, 1)

data[0] = []string{ip.IPAssignment.GetId(), ip.IPAssignment.GetAddress(), strconv.FormatBool(ip.IPAssignment.GetPublic()), ip.IPAssignment.CreatedAt.String()}
header := []string{"ID", "Address", "Public", "Created"}
data[0] = []string{ip.IPAssignment.GetId(), string(ip.IPAssignment.GetType()), ip.IPAssignment.GetAddress(), strconv.FormatBool(ip.IPAssignment.GetPublic()), ip.IPAssignment.CreatedAt.String()}
header := []string{"ID", "Type", "Address", "Public", "Created"}

return c.Out.Output(ip, header, &data)
} else if reservationID != "" {
ip, _, err := c.IPService.FindIPAddressById(context.Background(), reservationID).Include(inc).Exclude(exc).Execute()
if err != nil {
return fmt.Errorf("Could not get Reservation IP address: %w", err)
return fmt.Errorf("could not get Reservation IP address: %w", err)
}

data := make([][]string, 1)
code := ""
metro := ""
if ip.IPReservation.Facility != nil {
code = ip.IPReservation.Facility.GetCode()

if ip.IPReservation != nil {

if ip.IPReservation.Metro != nil {
metro = ip.IPReservation.Metro.GetCode()
}
data[0] = []string{ip.IPReservation.GetId(), string(ip.IPReservation.GetType()), ip.IPReservation.GetAddress(), metro, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()}
}

if ip.IPReservation.Metro != nil {
metro = ip.IPReservation.Metro.GetCode()
if ip.VrfIpReservation != nil {

if ip.VrfIpReservation.Metro != nil {
metro = ip.VrfIpReservation.Metro.GetCode()
}
data[0] = []string{ip.VrfIpReservation.GetId(), string(ip.VrfIpReservation.GetType()), ip.VrfIpReservation.GetAddress(), metro, strconv.FormatBool(ip.VrfIpReservation.GetPublic()), ip.VrfIpReservation.CreatedAt.String()}
}

data[0] = []string{ip.IPReservation.GetId(), ip.IPReservation.GetAddress(), metro, code, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()}
header := []string{"ID", "Address", "Metro", "Facility", "Public", "Created"}
header := []string{"ID", "Type", "Address", "Metro", "Public", "Created"}

return c.Out.Output(ip, header, &data)
}

resp, err := c.IPService.FindIPReservations(context.Background(), projectID).Include(inc).Exclude(exc).Types(types).ExecuteWithPagination()
if err != nil {
return fmt.Errorf("Could not list Project IP addresses: %w", err)
return fmt.Errorf("could not list Project IP addresses: %w", err)
}
ips := resp.IpAddresses
data := make([][]string, len(ips))

for i, ip := range ips {
code := ""
metro := ""
if ip.IPReservation.Facility != nil {
code = ip.IPReservation.Facility.GetCode()
if ip.IPReservation != nil {

if ip.IPReservation.Metro != nil {
metro = ip.IPReservation.Metro.GetCode()
}
data[i] = []string{ip.IPReservation.GetId(), string(ip.IPReservation.GetType()), ip.IPReservation.GetAddress(), metro, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()}
}
if ip.IPReservation.Metro != nil {
metro = ip.IPReservation.Metro.GetCode()
if ip.VrfIpReservation != nil {
if ip.VrfIpReservation.Metro != nil {
metro = ip.IPReservation.Metro.GetCode()
}
data[i] = []string{ip.VrfIpReservation.GetId(), string(ip.VrfIpReservation.GetType()), ip.VrfIpReservation.GetAddress(), metro, strconv.FormatBool(ip.VrfIpReservation.GetPublic()), ip.VrfIpReservation.CreatedAt.String()}
}
data[i] = []string{ip.IPReservation.GetId(), ip.IPReservation.GetAddress(), metro, code, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()}
}
header := []string{"ID", "Address", "Metro", "Facility", "Public", "Created"}
header := []string{"ID", "Type", "Address", "Metro", "Public", "Created"}

return c.Out.Output(ips, header, &data)
},
Expand Down
Loading
Loading