Skip to content

Commit

Permalink
prerelease - initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tchiapuziowong committed Jan 20, 2023
0 parents commit 959e842
Show file tree
Hide file tree
Showing 14 changed files with 1,620 additions and 0 deletions.
373 changes: 373 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Terraform Provider for AOS-CX

The Terraform Provider for AOS-CX provides a set of configuration management modules and resources specifically designed to manage/configure AOS-CX switches using REST API.


## Requirements

- [Terraform](https://www.terraform.io/downloads.html) >= 0.13.x
- [Go](https://golang.org/doc/install) >= 1.18
- Install AOS-CX Terraform Provider from Terraform Registry
- Terraform 0.13 added support for automatically downloading providers from the terraform registry. Add the following to your terraform project

```
terraform {
required_providers {
aoscx = {
version = "=> 1.0.0"
source = "aruba/aoscx"
}
}
}
```


## Using the AOS-CX provider

To use the AOS-CX Terraform provider you'll need to define the switch connection details inside a provider block with the following variables:
- `hostname`: IP address of the switch
- `username`: Username used to login to the switch using REST API
- `password`: Password used to login to the switch using REST API
- see Terraform's documentation on how to [Protect Sensitive Input Variables](https://developer.hashicorp.com/terraform/tutorials/configuration-language/sensitive-variables)
```
provider "aoscx" {
hostname = "10.6.7.16"
username = "admin"
password = "admin"
}
```

Once the provider is defined then you'll define the resources you want Terraform manage on your CX switch. To see all supported resources and their required/optional values see the [/docs](https://github.com/aruba/terraform-provider-aoscx/tree/master/docs) directory.

Here's an example:
```
resource "aoscx_vlan" "vlan42" {
vlan_id = 42
name = "terraform vlan"
}
resource "aoscx_interface" "int_1_1_14" {
name = "1/1/14"
admin_state = "down"
description = "terraform_uplink"
}
resource "aoscx_l2_interface" "int_1_1_15" {
interface = "1/1/15"
admin_state = "up"
description = "terraform_downlink"
vlan_mode = "access"
vlan_tag = 42
}
resource "aoscx_l2_interface" "int_1_1_16" {
interface = "1/1/16"
admin_state = "down"
vlan_mode = "trunk"
vlan_ids = [20, 42]
native_vlan_tag = true
}
```
88 changes: 88 additions & 0 deletions aoscx/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package aoscx

import (
"context"
"crypto/tls"
"net/http"

"github.com/aruba/aoscxgo"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type Aoscx struct {
hostname string
username string
password string
rest_version string
cookie *http.Cookie
}

func Provider() *schema.Provider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"hostname": {
Type: schema.TypeString,
Required: true,
Optional: false,
Description: "Hostname/IP address of the AOS-CX switch to connect to",
},
"username": {
Type: schema.TypeString,
Optional: false,
Required: true,
Description: "Username used to authenticate",
},
"password": {
Type: schema.TypeString,
Optional: false,
Required: true,
Description: "Password used to authenticate",
},
},
ResourcesMap: map[string]*schema.Resource{
"aoscx_vlan": resourceVlan(),
"aoscx_interface": resourceInterface(),
"aoscx_l2_interface": resourceL2Interface(),
},
ConfigureContextFunc: providerConfigure,
}
}

func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
hostname := d.Get("hostname").(string)
username := d.Get("username").(string)
password := d.Get("password").(string)

var diags diag.Diagnostics

if (hostname != "") && (username != "") && (password != "") {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}

sw, err := aoscxgo.Connect(
&aoscxgo.Client{
Hostname: hostname,
Username: username,
Password: password,
Transport: tr,
},
)

if (sw.Cookie == nil) || (err != nil) {
return nil, diag.FromErr(err)
}

return sw, diags
}

diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Unable to create AOS-CX client",
Detail: "Invalid or no values found for hostname, username, password",
})

return nil, diags
}
199 changes: 199 additions & 0 deletions aoscx/resource_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package aoscx

import (
"context"

"github.com/aruba/aoscxgo"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceInterface() *schema.Resource {
return &schema.Resource{
Description: "Resource to configure interfaces physical attributes on AOS-CX switches.",
CreateContext: resourceInterfaceCreate,
ReadContext: resourceInterfaceRead,
UpdateContext: resourceInterfaceUpdate,
DeleteContext: resourceInterfaceDelete,

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Required: false,
Optional: true,
},
"admin_state": &schema.Schema{
Type: schema.TypeString,
Required: false,
Default: "up",
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"up", "down"}, true),
},
},
}
}

func resourceInterfaceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
var err error

sw := m.(*aoscxgo.Client)

tmp_int := aoscxgo.Interface{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
AdminState: d.Get("admin_state").(string),
}

err = tmp_int.Create(sw)

//defer logout(tr, cookie, url)

if materialized := tmp_int.GetStatus(); !materialized {

err = tmp_int.Get(sw)

if err != nil {
{
diags = append(diags, diag.Errorf("Error in Creating Interface ", err)...)
return diags
}
}

diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "Interface Already Existing",
Detail: string(tmp_int.Name),
})

err = tmp_int.Update(sw)

if err != nil {
if err.(*aoscxgo.RequestError).StatusCode == "404 Not Found" {
diags = append(diags, diag.Errorf("Error Updating Interface does not exist: %s", err.(*aoscxgo.RequestError).StatusCode)...)
return diags
} else if err.(*aoscxgo.RequestError).StatusCode != "204 No Content" {
diags = append(diags, diag.Errorf("Error in Updating Interface: %s", err.(*aoscxgo.RequestError).StatusCode)...)
return diags
}
}

}

d.SetId(d.Get("name").(string))
d.Set("name", d.Get("name").(string))

resourceInterfaceRead(ctx, d, m)

return diags
}

func resourceInterfaceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
var err error

sw := m.(*aoscxgo.Client)

// Retrieve Interface from sw if existing
tmp_int := aoscxgo.Interface{
Name: d.Get("name").(string),
}
//tmp_vlan.GetStatus() will return if existing
err = tmp_int.Get(sw)

if err != nil {
//Failure in VLAN retrieval
d.SetId("")
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "Interface Not Found",
Detail: "Interface Not Found",
})
return diags
}

d.Set("name", tmp_int.Name)
d.Set("description", tmp_int.Description)
d.Set("admin_state", tmp_int.AdminState)

return diags
}

func resourceInterfaceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
var err error

sw := m.(*aoscxgo.Client)

// Retrieve Interface from sw if existing
tmp_int := aoscxgo.Interface{
Name: d.Get("name").(string),
}
//tmp_vlan.GetStatus() will return if existing
err = tmp_int.Get(sw)

if d.HasChange("description") {
tmp_int.Description = d.Get("description").(string)
}

if d.HasChange("admin_state") {
tmp_state := d.Get("admin_state").(string)
if tmp_state == "" || (tmp_state != "down" && tmp_state != "up") {
tmp_int.AdminState = "down"
d.Set("admin_state", "down")
// Should enforce value can only be up or down
}
tmp_int.AdminState = tmp_state
}

err = tmp_int.Update(sw)

if err != nil {
if err.(*aoscxgo.RequestError).StatusCode == "404 Not Found" {
diags = append(diags, diag.Errorf("Error Updating Interface does not exist: %s", err.(*aoscxgo.RequestError).StatusCode)...)
return diags
} else if err.(*aoscxgo.RequestError).StatusCode != "204 No Content" {
diags = append(diags, diag.Errorf("Error in Updating Interface: %s", err.(*aoscxgo.RequestError).StatusCode)...)
return diags
}
}

return resourceInterfaceRead(ctx, d, m)
}

func resourceInterfaceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
var err error

sw := m.(*aoscxgo.Client)

// Create Interface Obj
tmp_int := aoscxgo.Interface{
Name: d.Get("name").(string),
}

err = tmp_int.Delete(sw)

if err != nil {
if err.(*aoscxgo.RequestError).StatusCode == "404 Not Found" {
diags = append(diags, diag.Errorf("Error Updating Interface does not exist: %s", err.(*aoscxgo.RequestError).StatusCode)...)
return diags
} else if err.(*aoscxgo.RequestError).StatusCode != "204 No Content" {
diags = append(diags, diag.Errorf("Error in Updating Interface: %s ", err.(*aoscxgo.RequestError).StatusCode)...)
return diags
}
}

d.SetId("")
return nil
}
Loading

0 comments on commit 959e842

Please sign in to comment.