Skip to content

Commit

Permalink
feat: Enabled VRF IP reservations
Browse files Browse the repository at this point in the history
  • Loading branch information
codinja1188 authored and cprivitere committed Jan 19, 2024
1 parent 3b9a618 commit 930336f
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 71 deletions.
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"}

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")

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 @@ -39,7 +39,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 @@ -69,57 +69,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)
}

ips, err := pager.GetAllIPReservations(c.IPService, projectID, inc, exc, types)
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 := ipsList.GetIpAddresses()
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

0 comments on commit 930336f

Please sign in to comment.