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 committed Jan 19, 2024
1 parent d503aec commit 90935e1
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 65 deletions.
7 changes: 7 additions & 0 deletions docs/metal_ip_request.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,26 @@ metal ip request -p <project_id> -t <ip_address_type> -q <quantity> (-m <metro>
```
# 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.
-t, --type string The type of IP Address, either public_ipv4 or global_ipv4.
-v, --vrfID 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 <VrfID] [-p <ip_id>] [flags]
```

### Examples

```
# Retrieves the list of VRF IP Reservations for the VRF..
metal vrf ips [-v <VrfID]
# Retrieve a specific IP Reservation for a VRF
metal vrf ips [-v <VrfID] [-p <ip_id>]
```

### Options

```
-h, --help help for ips
-p, --ipID string Specify the IP UUID to retrieve the details of a VRF IP reservation.
-v, --vrfID 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: 87 additions & 46 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,13 +14,18 @@ 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
Expand All @@ -46,35 +34,84 @@ func (c *Client) Request() *cobra.Command {
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.
typeFlag, _ := cmd.Flags().GetString("type")

req := &metal.IPReservationRequestInput{
Metro: &metro,
Tags: tags,
Quantity: int32(quantity),
Type: ttype,
Facility: &facility,
}
if typeFlag != "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.
cidrFlag, _ := cmd.Flags().GetInt("cidr")
networkFlag, _ := cmd.Flags().GetString("network")
vrfIdFlag, _ := cmd.Flags().GetString("vrfID")
if cidrFlag == 0 || networkFlag == "" || vrfIdFlag == "" {
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(cidrFlag),
Network: networkFlag,
VrfId: vrfIdFlag,
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 typeFlag != "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 @@ -86,10 +123,14 @@ func (c *Client) Request() *cobra.Command {
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().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, "vrfID", "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
50 changes: 31 additions & 19 deletions internal/ips/retrieve.go
Original file line number Diff line number Diff line change
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 90935e1

Please sign in to comment.