From 099de941db8e92c3125786830b42ea502f1df4eb Mon Sep 17 00:00:00 2001 From: chinthalapalli Date: Fri, 13 Oct 2023 00:08:09 +0530 Subject: [PATCH] adding devel changes --- docs/index.md | 1 + docs/resources/lag.md | 55 +++ examples/resources/f5os_lag/import.sh | 2 + go.mod | 4 +- go.sum | 8 +- internal/provider/common.go | 2 +- internal/provider/interface_resource.go | 18 +- internal/provider/lag_resource.go | 299 ++++++++++++ internal/provider/lag_resource_test.go | 134 ++++++ internal/provider/partition_resource.go | 18 +- internal/provider/provider.go | 54 ++- internal/provider/tenant_image_data_source.go | 9 +- internal/provider/tenant_resource.go | 15 +- internal/provider/vlan_resource.go | 17 +- vendor/github.com/google/uuid/CHANGELOG.md | 10 + vendor/github.com/google/uuid/CONTRIBUTING.md | 16 + vendor/github.com/google/uuid/README.md | 10 +- vendor/github.com/google/uuid/node_js.go | 2 +- vendor/github.com/google/uuid/uuid.go | 10 +- .../terraform-providers/f5osclient/f5os.go | 432 ++++++++++++++++-- .../f5osclient/structs_partition.go | 88 +++- .../f5osclient/teemclient.go | 100 ++++ vendor/modules.txt | 4 +- 23 files changed, 1213 insertions(+), 95 deletions(-) create mode 100644 docs/resources/lag.md create mode 100644 examples/resources/f5os_lag/import.sh create mode 100644 internal/provider/lag_resource.go create mode 100644 internal/provider/lag_resource_test.go create mode 100644 vendor/github.com/google/uuid/CHANGELOG.md create mode 100644 vendor/gitswarm.f5net.com/terraform-providers/f5osclient/teemclient.go diff --git a/docs/index.md b/docs/index.md index faff248..ac40319 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,4 +32,5 @@ provider "f5os" { - `host` (String) URI/Host details for F5os Device,can be provided via `F5OS_HOST` environment variable. - `password` (String, Sensitive) Password for F5os Device,can be provided via `F5OS_PASSWORD` environment variable. - `port` (Number) Port Number to be used to make API calls to HOST +- `teem_disable` (Boolean) If this flag set to true,sending telemetry data to TEEM will be disabled,can be provided via `TEEM_DISABLE` environment variable. - `username` (String) Username for F5os Device,can be provided via `F5OS_USERNAME` environment variable.User provided here need to have required permission as per [UserManagement](https://techdocs.f5.com/en-us/f5os-a-1-4-0/f5-rseries-systems-administration-configuration/title-user-mgmt.html) diff --git a/docs/resources/lag.md b/docs/resources/lag.md new file mode 100644 index 0000000..cbfad1a --- /dev/null +++ b/docs/resources/lag.md @@ -0,0 +1,55 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "f5os_lag Resource - terraform-provider-f5os" +subcategory: "" +description: |- + Resource to Manage network Link Aggregation Group (LAG) interfaces on F5OS systems like VELOS chassis partitions or rSeries platforms +--- + +# f5os_lag (Resource) + +Resource to Manage network Link Aggregation Group (LAG) interfaces on F5OS systems like VELOS chassis partitions or rSeries platforms + +## Example Usage + +```terraform +resource "f5os_lag" "test_lag" { + name = "test_lag" + members = ["1.0"] + native_vlan = 5 + trunk_vlans = [ + 1, + 2, + 3 + ] +} +``` + + +## Schema + +### Required + +- `name` (String) Name of the Link Aggregation Group interface (LAG) interface to configure + +### Optional + +- `members` (List of String) List of physical interfaces that are members of the LAG. +- `native_vlan` (Number) Configures the VLAN ID to associate with LAG interface. +The `native_vlan` parameter is used for untagged traffic. +- `trunk_vlans` (List of Number) Configures multiple VLAN IDs to associate with the LAG interface. +The `trunk_vlans` parameter is used for tagged traffic + +### Read-Only + +- `id` (String) Unique identifier for LAG Interface resource. +- `status` (String) Operational state of the LAG interface. + +## Import + +Import is supported using the following syntax: + +```shell +# LAG Interface can be imported by specifying the LAG Interface name +terraform import f5os_lag.test-import test-lag +``` diff --git a/examples/resources/f5os_lag/import.sh b/examples/resources/f5os_lag/import.sh new file mode 100644 index 0000000..726135e --- /dev/null +++ b/examples/resources/f5os_lag/import.sh @@ -0,0 +1,2 @@ +# LAG Interface can be imported by specifying the LAG Interface name +terraform import f5os_lag.test-import test-lag \ No newline at end of file diff --git a/go.mod b/go.mod index e36d78f..32c86f8 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.8.0 github.com/hashicorp/terraform-plugin-testing v1.2.0 github.com/stretchr/testify v1.8.4 - gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2-0.20230817082623-065c38e0fe3e + gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2 ) require ( @@ -25,7 +25,7 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect diff --git a/go.sum b/go.sum index ced172c..6b3338a 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -211,8 +211,8 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.13.1 h1:0a6bRwuiSHtAmqCqNOE+c2oHgepv0ctoxU4FUe43kwc= github.com/zclconf/go-cty v1.13.1/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= -gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2-0.20230817082623-065c38e0fe3e h1:csKWYvSpNYrGa2drcwr6zvtqcuJ9zuH8HNfvGrPkg3o= -gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2-0.20230817082623-065c38e0fe3e/go.mod h1:KN91vHAGPLQq+i+05CQXVcyqwjOpoJdq8ksPB7hR9MA= +gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2 h1:RXDYEeXCYymbdPnbOt2XVQLVK2tYXWpfrQMZ607jDSQ= +gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2/go.mod h1:XmqFs5vsbNni7scPBmJ0pWDIFa8i1NOnHoLnQPU/c98= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/internal/provider/common.go b/internal/provider/common.go index 32935da..02274ea 100644 --- a/internal/provider/common.go +++ b/internal/provider/common.go @@ -12,7 +12,7 @@ func extractSubnet(cidr string) (int, string, error) { return ones, ip.String(), nil } -func getSliceDifference(slice1, slice2 []int64) []int64 { +func getIntSliceDifference(slice1, slice2 []int64) []int64 { var diff []int64 for _, num1 := range slice1 { found := false diff --git a/internal/provider/interface_resource.go b/internal/provider/interface_resource.go index a36d0d9..c5211b5 100644 --- a/internal/provider/interface_resource.go +++ b/internal/provider/interface_resource.go @@ -24,7 +24,8 @@ func NewInterfaceResource() resource.Resource { // InterfaceResource defines the resource implementation. type InterfaceResource struct { - client *f5ossdk.F5os + client *f5ossdk.F5os + teemData *TeemData } type InterfaceResourceModel struct { @@ -82,6 +83,9 @@ func (r *InterfaceResource) Schema(ctx context.Context, req resource.SchemaReque func (r *InterfaceResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { r.client, resp.Diagnostics = toF5osProvider(req.ProviderData) + teemData.ProviderName = "f5os" + teemData.ResourceName = "f5os_interface" + r.teemData = teemData } func (r *InterfaceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -110,7 +114,13 @@ func (r *InterfaceResource) Create(ctx context.Context, req resource.CreateReque tflog.Debug(ctx, fmt.Sprintf("interfaceReqConfig Response:%+v", string(respByte))) data.Id = types.StringValue(data.Name.ValueString()) - + teemInfo := make(map[string]interface{}) + teemInfo["teemData"] = r.teemData + r.client.Metadata = teemInfo + err = r.client.SendTeem(teemInfo) + if err != nil { + resp.Diagnostics.AddError("Teem Error", fmt.Sprintf("Sending Teem Data failed: %s", err)) + } intfData, err := r.client.GetInterface(data.Name.ValueString()) if err != nil { resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to Read/Get Interface, got error: %s", err)) @@ -217,7 +227,9 @@ func (r *InterfaceResource) interfaceResourceModelToState(ctx context.Context, r data.Name = types.StringValue(respData.OpenconfigInterfacesInterface[0].Name) data.Enabled = types.BoolValue(respData.OpenconfigInterfacesInterface[0].State.Enabled) data.Status = types.StringValue(respData.OpenconfigInterfacesInterface[0].State.OperStatus) - data.NativeVlan = types.Int64Value(int64(respData.OpenconfigInterfacesInterface[0].OpenconfigIfEthernetEthernet.OpenconfigVlanSwitchedVlan.Config.NativeVlan)) + if int64(respData.OpenconfigInterfacesInterface[0].OpenconfigIfEthernetEthernet.OpenconfigVlanSwitchedVlan.Config.NativeVlan) != 0 { + data.NativeVlan = types.Int64Value(int64(respData.OpenconfigInterfacesInterface[0].OpenconfigIfEthernetEthernet.OpenconfigVlanSwitchedVlan.Config.NativeVlan)) + } data.TrunkVlans, _ = types.ListValueFrom(ctx, types.Int64Type, respData.OpenconfigInterfacesInterface[0].OpenconfigIfEthernetEthernet.OpenconfigVlanSwitchedVlan.Config.TrunkVlans) } diff --git a/internal/provider/lag_resource.go b/internal/provider/lag_resource.go new file mode 100644 index 0000000..2882264 --- /dev/null +++ b/internal/provider/lag_resource.go @@ -0,0 +1,299 @@ +package provider + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + f5ossdk "gitswarm.f5net.com/terraform-providers/f5osclient" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &LagResource{} +var _ resource.ResourceWithImportState = &LagResource{} + +func NewLagResource() resource.Resource { + return &LagResource{} +} + +// LagResource defines the resource implementation. +type LagResource struct { + client *f5ossdk.F5os +} + +type LagResourceModel struct { + Name types.String `tfsdk:"name"` + NativeVlan types.Int64 `tfsdk:"native_vlan"` + TrunkVlans types.List `tfsdk:"trunk_vlans"` + Status types.String `tfsdk:"status"` + Members types.List `tfsdk:"members"` + Id types.String `tfsdk:"id"` +} + +func (r *LagResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_lag" +} + +func (r *LagResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Resource to Manage network Link Aggregation Group (LAG) interfaces on F5OS systems like VELOS chassis partitions or rSeries platforms", + + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the Link Aggregation Group interface (LAG) interface to configure", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "native_vlan": schema.Int64Attribute{ + MarkdownDescription: "Configures the VLAN ID to associate with LAG interface.\nThe `native_vlan` parameter is used for untagged traffic.", + Optional: true, + }, + "trunk_vlans": schema.ListAttribute{ + MarkdownDescription: "Configures multiple VLAN IDs to associate with the LAG interface.\nThe `trunk_vlans` parameter is used for tagged traffic", + Optional: true, + ElementType: types.Int64Type, + }, + "members": schema.ListAttribute{ + MarkdownDescription: "List of physical interfaces that are members of the LAG.", + Optional: true, + Computed: true, + ElementType: types.StringType, + }, + "status": schema.StringAttribute{ + MarkdownDescription: "Operational state of the LAG interface.", + Computed: true, + }, + "id": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Unique identifier for LAG Interface resource.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +func (r *LagResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.client, resp.Diagnostics = toF5osProvider(req.ProviderData) +} + +func (r *LagResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data *LagResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + if r.client.PlatformType == "Velos Controller" { + resp.Diagnostics.AddError("Client Error", "`f5os_lag` resource is supported with Velos Partition level/rSeries appliance.") + return + } + tflog.Info(ctx, fmt.Sprintf("[CREATE] Config LAG Interface :%+v", data.Name.ValueString())) + interfaceReqConfig := getLagInterfaceConfig(ctx, data) + + tflog.Debug(ctx, fmt.Sprintf("lagInterfaceReqConfig Data:%+v", interfaceReqConfig)) + + membersConfig := getLagMembersConfig(ctx, data) + + respByte, err := r.client.CreateLagInterface(interfaceReqConfig, membersConfig) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error:", fmt.Sprintf("Creating LAG interface failed, got error: %s", err)) + return + } + + tflog.Debug(ctx, fmt.Sprintf("lagInterfaceReqConfig Response:%+v", string(respByte))) + data.Id = types.StringValue(data.Name.ValueString()) + + intfData, err := r.client.GetLagInterface(data.Name.ValueString()) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to Read/Get LAG Interface, got error: %s", err)) + return + } + tflog.Debug(ctx, fmt.Sprintf("LAG interface Resp :%+v", intfData)) + r.lagInterfaceResourceModelToState(ctx, intfData, data) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + +} + +func (r *LagResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *LagResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + tflog.Info(ctx, fmt.Sprintf("[READ] Reading LAG interface :%+v", data.Id.ValueString())) + + intfData, err := r.client.GetLagInterface(data.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to Read/Get LAG interface, got error: %s", err)) + return + } + tflog.Debug(ctx, fmt.Sprintf("LAG interface Resp :%+v", intfData)) + r.lagInterfaceResourceModelToState(ctx, intfData, data) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *LagResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data *LagResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if r.client.PlatformType == "Velos Controller" { + resp.Diagnostics.AddError("Client Error", "`f5os_lag` resource is supported with Velos Partition level/rSeries appliance.") + return + } + tflog.Info(ctx, fmt.Sprintf("[UPDATE] Config LAG interface :%+v", data.Name.ValueString())) + lagInterfaceReqConfig := getLagInterfaceConfig(ctx, data) + tflog.Info(ctx, fmt.Sprintf("lagInterfaceReqConfig Data:%+v", lagInterfaceReqConfig)) + + if !data.Members.IsNull() && !data.Members.IsUnknown() { + memberData, err := r.client.GetLagInterface(data.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to Read LAG interface members got error: %s", err)) + return + } + if memberData != nil { + var haveMembers []string + for _, member := range memberData.OpenconfigInterfacesInterface[0].OpenconfigIfAggregateAggregation.State.Members.Member { + haveMembers = append(haveMembers, member.Name) + } + err := r.client.RemoveLagMembers(haveMembers) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to remove members from LAG interface, got error: %s", err)) + return + } + membersConfig := getLagMembersConfig(ctx, data) + _, err = r.client.UpdateLagMembers(membersConfig) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to update LAG interface members, got error: %s", err)) + return + } + } + } + + respByte, err := r.client.UpdateLagInterface(data.Id.ValueString(), lagInterfaceReqConfig) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error:", fmt.Sprintf("Update LAG interface failed, got error: %s", err)) + return + } + tflog.Info(ctx, fmt.Sprintf("lagInterfaceReqConfig Response:%+v", string(respByte))) + + data.Id = types.StringValue(data.Name.ValueString()) + + intfData, err := r.client.GetLagInterface(data.Name.ValueString()) + if err != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to Read/Get LAG Interface, got error: %s", err)) + return + } + tflog.Debug(ctx, fmt.Sprintf("LAG interface Resp :%+v", intfData)) + r.lagInterfaceResourceModelToState(ctx, intfData, data) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *LagResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *LagResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + // Check if we have any physical interfaces that are a member of the LAG interface + memberData, err1 := r.client.GetLagInterface(data.Id.ValueString()) + if err1 != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to Read LAG interface members got error: %s", err1)) + return + } + // Remove any associated interfaces + if memberData != nil { + var haveMembers []string + for _, member := range memberData.OpenconfigInterfacesInterface[0].OpenconfigIfAggregateAggregation.State.Members.Member { + haveMembers = append(haveMembers, member.Name) + } + err := r.client.RemoveLagMembers(haveMembers) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Removing LAG interface member failed, got error: %s", err)) + return + } + } + + err2 := r.client.RemoveLagInterface(data.Id.ValueString()) + if err2 != nil { + resp.Diagnostics.AddError("F5OS Client Error", fmt.Sprintf("Unable to delete LAG interface, got error: %s", err2)) + return + } + +} + +func (r *LagResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *LagResource) lagInterfaceResourceModelToState(ctx context.Context, respData *f5ossdk.F5RespLagInterfaces, data *LagResourceModel) { + data.Name = types.StringValue(respData.OpenconfigInterfacesInterface[0].Name) + data.NativeVlan = types.Int64Value(int64(respData.OpenconfigInterfacesInterface[0].OpenconfigIfAggregateAggregation.OpenconfigVlanSwitchedVlan.Config.NativeVlan)) + data.TrunkVlans, _ = types.ListValueFrom(ctx, types.Int64Type, respData.OpenconfigInterfacesInterface[0].OpenconfigIfAggregateAggregation.OpenconfigVlanSwitchedVlan.Config.TrunkVlans) + data.Status = types.StringValue(respData.OpenconfigInterfacesInterface[0].State.OperStatus) + + var members []string + for _, member := range respData.OpenconfigInterfacesInterface[0].OpenconfigIfAggregateAggregation.State.Members.Member { + members = append(members, member.Name) + } + data.Members, _ = types.ListValueFrom(ctx, types.StringType, members) +} + +func getLagInterfaceConfig(ctx context.Context, data *LagResourceModel) *f5ossdk.F5ReqLagInterfaces { + interfaceReq := f5ossdk.F5ReqLagInterface{} + interfaceReq.Name = data.Name.ValueString() + interfaceReq.Config.Name = data.Name.ValueString() + interfaceReq.Config.Type = "iana-if-type:ieee8023adLag" + interfaceReq.Config.Enabled = true + interfaceReq.OpenconfigIfAggregateAggregation.Config.LagType = "LACP" + interfaceReq.OpenconfigIfAggregateAggregation.Config.DistributioHash = "src-dst-ipport" + interfaceReq.OpenconfigIfAggregateAggregation.OpenconfigVlanSwitchedVlan.Config.NativeVlan = int(data.NativeVlan.ValueInt64()) + var trunkIds []int + data.TrunkVlans.ElementsAs(ctx, &trunkIds, false) + interfaceReq.OpenconfigIfAggregateAggregation.OpenconfigVlanSwitchedVlan.Config.TrunkVlans = trunkIds + interfaceOpenconfigReq := f5ossdk.F5ReqLagInterfaces{} + interfaceOpenconfigReq.OpenconfigInterfacesInterfaces.Interface = append(interfaceOpenconfigReq.OpenconfigInterfacesInterfaces.Interface, interfaceReq) + return &interfaceOpenconfigReq +} + +func getLagMembersConfig(ctx context.Context, data *LagResourceModel) *f5ossdk.F5ReqLagInterfaces { + memberConfigReq := f5ossdk.F5ReqLagInterfaces{} + memberReq := f5ossdk.F5ReqLagInterface{} + var members []string + data.Members.ElementsAs(ctx, &members, false) + for _, member := range members { + memberReq.Name = member + memberReq.Config.Name = member + memberReq.OpenconfigIfEthernetEthernet.Config.Name = data.Name.ValueString() + memberConfigReq.OpenconfigInterfacesInterfaces.Interface = append(memberConfigReq.OpenconfigInterfacesInterfaces.Interface, memberReq) + } + return &memberConfigReq +} diff --git a/internal/provider/lag_resource_test.go b/internal/provider/lag_resource_test.go new file mode 100644 index 0000000..6492de2 --- /dev/null +++ b/internal/provider/lag_resource_test.go @@ -0,0 +1,134 @@ +package provider + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccLagInterfaceCreateTC1Resource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Read testing + { + Config: testAccLagInterfaceCreateResourceConfig, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("f5os_vlan.vlan10", "vlan_id", "10"), + resource.TestCheckResourceAttr("f5os_vlan.vlan11", "vlan_id", "11"), + resource.TestCheckResourceAttr("f5os_vlan.vlan12", "vlan_id", "12"), + resource.TestCheckResourceAttr("f5os_vlan.vlan13", "vlan_id", "13"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "native_vlan", "13"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.0", "10"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.1", "11"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.2", "12"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "members.0", "1.0"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "members.1", "2.0"), + ), + }, + // ImportState testing + { + ResourceName: "f5os_lag.test_lag", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccLagInterfaceCreateTC2Resource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Read testing + { + Config: testAccLagInterfaceCreateResourceConfig, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("f5os_vlan.vlan10", "vlan_id", "10"), + resource.TestCheckResourceAttr("f5os_vlan.vlan11", "vlan_id", "11"), + resource.TestCheckResourceAttr("f5os_vlan.vlan12", "vlan_id", "12"), + resource.TestCheckResourceAttr("f5os_vlan.vlan13", "vlan_id", "13"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "native_vlan", "13"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.0", "10"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.1", "11"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.2", "12"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "members.0", "1.0"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "members.1", "2.0"), + ), + }, + { + Config: testAccLagInterfaceCreateTC2ResourceConfig, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("f5os_vlan.vlan10", "vlan_id", "10"), + resource.TestCheckResourceAttr("f5os_vlan.vlan11", "vlan_id", "11"), + resource.TestCheckResourceAttr("f5os_vlan.vlan12", "vlan_id", "12"), + resource.TestCheckResourceAttr("f5os_vlan.vlan13", "vlan_id", "13"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "native_vlan", "11"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.0", "10"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.1", "12"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "trunk_vlans.2", "13"), + resource.TestCheckResourceAttr("f5os_lag.test_lag", "members.0", "2.0"), + ), + }, + }, + }) +} + +const testAccLagInterfaceCreateResourceConfig = ` +resource "f5os_vlan" "vlan10" { + vlan_id = 10 + name = "vlan10" +} +resource "f5os_vlan" "vlan11" { + vlan_id = 11 + name = "vlan11" +} +resource "f5os_vlan" "vlan12" { + vlan_id = 12 + name = "vlan12" +} +resource "f5os_vlan" "vlan13" { + vlan_id = 13 + name = "vlan13" +} +resource "f5os_lag" "test_lag" { + name = "tf-lag" + native_vlan = f5os_vlan.vlan13.vlan_id + trunk_vlans = [ + f5os_vlan.vlan10.vlan_id, + f5os_vlan.vlan11.vlan_id, + f5os_vlan.vlan12.vlan_id + ] + members = ["1.0", "2.0"] +} +` +const testAccLagInterfaceCreateTC2ResourceConfig = ` +resource "f5os_vlan" "vlan10" { + vlan_id = 10 + name = "vlan10" +} +resource "f5os_vlan" "vlan11" { + vlan_id = 11 + name = "vlan11" +} +resource "f5os_vlan" "vlan12" { + vlan_id = 12 + name = "vlan12" +} +resource "f5os_vlan" "vlan13" { + vlan_id = 13 + name = "vlan13" +} +resource "f5os_lag" "test_lag" { + name = "tf-lag" + native_vlan = f5os_vlan.vlan11.vlan_id + trunk_vlans = [ + f5os_vlan.vlan10.vlan_id, + f5os_vlan.vlan12.vlan_id, + f5os_vlan.vlan13.vlan_id + ] + members = ["2.0"] +} +` diff --git a/internal/provider/partition_resource.go b/internal/provider/partition_resource.go index e610749..b98d8bf 100644 --- a/internal/provider/partition_resource.go +++ b/internal/provider/partition_resource.go @@ -31,7 +31,8 @@ func NewPartitionResource() resource.Resource { // PartitionResource defines the resource implementation. type PartitionResource struct { - client *f5ossdk.F5os + client *f5ossdk.F5os + teemData *TeemData } type PartitionResourceModel struct { @@ -166,6 +167,9 @@ func (r *PartitionResource) Schema(ctx context.Context, req resource.SchemaReque func (r *PartitionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { r.client, resp.Diagnostics = toF5osProvider(req.ProviderData) + teemData.ProviderName = "f5os" + teemData.ResourceName = "f5os_partition" + r.teemData = teemData } func (r *PartitionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -179,7 +183,7 @@ func (r *PartitionResource) Create(ctx context.Context, req resource.CreateReque } if r.client.PlatformType != "Velos Controller" { - resp.Diagnostics.AddError("Client Error", "`f5os_partition` resource is supported on Velos Controllers only") + resp.Diagnostics.AddError("F5OS Client Error", "`f5os_partition` resource is supported on Velos Controllers only") return } @@ -237,7 +241,13 @@ func (r *PartitionResource) Create(ctx context.Context, req resource.CreateReque data.Slots = slots } - + teemInfo := make(map[string]interface{}) + teemInfo["teemData"] = r.teemData + r.client.Metadata = teemInfo + err = r.client.SendTeem(teemInfo) + if err != nil { + resp.Diagnostics.AddError("Teem Error", fmt.Sprintf("Sending Teem Data failed: %s", err)) + } // Save data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) @@ -314,7 +324,7 @@ func (r *PartitionResource) Update(ctx context.Context, req resource.UpdateReque // first we determine if a subset of slots on partition are not included in user data, and if yes we remove them first var slots []int64 data.Slots.ElementsAs(ctx, &slots, false) - slotDiff := getSliceDifference(slotData, slots) + slotDiff := getIntSliceDifference(slotData, slots) if len(slotDiff) > 0 { _, err := r.client.SetSlot("none", slotDiff) if err != nil { diff --git a/internal/provider/provider.go b/internal/provider/provider.go index d205be9..e43dcb2 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -28,11 +28,23 @@ type F5osProvider struct { // F5osProviderModel describes the provider data model. type F5osProviderModel struct { - Host types.String `tfsdk:"host"` - Username types.String `tfsdk:"username"` - Password types.String `tfsdk:"password"` - Port types.Int64 `tfsdk:"port"` + Host types.String `tfsdk:"host"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + Port types.Int64 `tfsdk:"port"` + TeemDisable types.Bool `tfsdk:"teem_disable"` } +type TeemData struct { + ResourceName string + ProviderName string + ProviderVersion string + TerraformVersion string + F5Platform string + F5SoftwareVersion string + TerraformLicense string +} + +var teemData = &TeemData{} func (p *F5osProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { resp.TypeName = "f5os" @@ -60,6 +72,10 @@ func (p *F5osProvider) Schema(ctx context.Context, req provider.SchemaRequest, r MarkdownDescription: "Port Number to be used to make API calls to HOST", Optional: true, }, + "teem_disable": schema.BoolAttribute{ + MarkdownDescription: "If this flag set to true,sending telemetry data to TEEM will be disabled,can be provided via `TEEM_DISABLE` environment variable.", + Optional: true, + }, }, } } @@ -70,22 +86,21 @@ func (p *F5osProvider) Configure(ctx context.Context, req provider.ConfigureRequ // Retrieve provider data from configuration var config F5osProviderModel resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - if resp.Diagnostics.HasError() { return } - // Configuration values are now available. - // if data.Endpoint.IsNull() { /* ... */ } - - // Default values to environment variables, but override - // with Terraform configuration value if set. - host := os.Getenv("F5OS_HOST") username := os.Getenv("F5OS_USERNAME") password := os.Getenv("F5OS_PASSWORD") - hostPort := 8888 + teemTmp := os.Getenv("TEEM_DISABLE") + hostPort := 8888 + var teemDisable bool + teemDisable = false + if teemTmp == "true" { + teemDisable = true + } if !config.Host.IsNull() { host = config.Host.ValueString() } @@ -100,6 +115,9 @@ func (p *F5osProvider) Configure(ctx context.Context, req provider.ConfigureRequ if !config.Port.IsNull() { hostPort = int(config.Port.ValueInt64()) } + if !config.TeemDisable.IsNull() { + teemDisable = config.TeemDisable.ValueBool() + } if host == "" { resp.Diagnostics.AddError( "Missing 'host' in provider configuration", @@ -142,6 +160,16 @@ func (p *F5osProvider) Configure(ctx context.Context, req provider.ConfigureRequ ) return } + client.Teem = teemDisable + teemData.TerraformVersion = req.TerraformVersion + teemData.ProviderName = "f5os" + teemData.ProviderVersion = p.version + teemData.F5Platform = fmt.Sprintf("F5OS %s", client.PlatformType) + teemData.F5SoftwareVersion = client.PlatformVersion + teemData.TerraformLicense = "open" + if req.TerraformVersion > "1.5.0" { + teemData.TerraformLicense = "business" + } resp.DataSourceData = client resp.ResourceData = client tflog.Info(ctx, "Configured F5OS client", map[string]any{"success": true}) @@ -156,6 +184,7 @@ func (p *F5osProvider) Resources(ctx context.Context) []func() resource.Resource NewVlanResource, NewInterfaceResource, NewCfgBackupResource, + NewLagResource, } } @@ -181,7 +210,6 @@ func toF5osProvider(in any) (*f5ossdk.F5os, diag.Diagnostics) { } var diags diag.Diagnostics - p, ok := in.(*f5ossdk.F5os) if !ok { diff --git a/internal/provider/tenant_image_data_source.go b/internal/provider/tenant_image_data_source.go index 514c9c5..3f4eab8 100644 --- a/internal/provider/tenant_image_data_source.go +++ b/internal/provider/tenant_image_data_source.go @@ -3,7 +3,6 @@ package provider import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/types" @@ -21,7 +20,8 @@ func NewImageInfoDataSource() datasource.DataSource { // ImageInfoDataSource defines the data source implementation. type ImageInfoDataSource struct { - client *f5ossdk.F5os + client *f5ossdk.F5os + teemData *TeemData } // ImageInfoDataSourceModel describes the data source data model. @@ -33,6 +33,10 @@ type ImageInfoDataSourceModel struct { func (d *ImageInfoDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_tenant_image" + teemData := &TeemData{} + teemData.ProviderName = req.ProviderTypeName + teemData.ResourceName = resp.TypeName + d.teemData = teemData } func (d *ImageInfoDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { @@ -90,6 +94,7 @@ func (d *ImageInfoDataSource) Read(ctx context.Context, req datasource.ReadReque data.ID = types.StringValue(data.ImageName.ValueString()) data.ImageName = types.StringValue(imageObj.TenantImages[0].Name) data.ImageStatus = types.StringValue(imageObj.TenantImages[0].Status) + teemData.ResourceName = "f5os_tenant_image" // Save data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) diff --git a/internal/provider/tenant_resource.go b/internal/provider/tenant_resource.go index 21bb9fd..692a2c2 100644 --- a/internal/provider/tenant_resource.go +++ b/internal/provider/tenant_resource.go @@ -37,7 +37,8 @@ func NewTenantResource() resource.Resource { // TenantResource defines the resource implementation. type TenantResource struct { - client *f5ossdk.F5os + client *f5ossdk.F5os + teemData *TeemData } // TenantResourceModel describes the resource data model. @@ -202,6 +203,9 @@ func (r *TenantResource) Schema(ctx context.Context, req resource.SchemaRequest, func (r *TenantResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { r.client, resp.Diagnostics = toF5osProvider(req.ProviderData) + teemData.ProviderName = "f5os" + teemData.ResourceName = "f5os_tenant" + r.teemData = teemData } func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -213,6 +217,7 @@ func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, if resp.Diagnostics.HasError() { return } + tflog.Info(ctx, fmt.Sprintf("[CREATE] Tenant:%+v", data.Name.ValueString())) if r.client.PlatformType == "Velos Controller" { resp.Diagnostics.AddError("Unsupported platform for resource", "`f5os_tenant` resource is supported with Velos Partition level (or) rSeries appliance") return @@ -247,6 +252,13 @@ func (r *TenantResource) Create(ctx context.Context, req resource.CreateRequest, tflog.Info(ctx, fmt.Sprintf("tenantConfig Data:%+v", tenantConfig)) mutex.Lock() + teemInfo := make(map[string]interface{}) + teemInfo["teemData"] = r.teemData + r.client.Metadata = teemInfo + err = r.client.SendTeem(teemInfo) + if err != nil { + resp.Diagnostics.AddError("Teem Error", fmt.Sprintf("Sending Teem Data failed: %s", err)) + } respByte, err := r.client.CreateTenant(tenantConfig, int(data.Timeout.ValueInt64())) if err != nil { resp.Diagnostics.AddError("F5OS Client Error:", fmt.Sprintf("Tenant Deploy failed, got error: %s", err)) @@ -280,7 +292,6 @@ func (r *TenantResource) Read(ctx context.Context, req resource.ReadRequest, res if resp.Diagnostics.HasError() { return } - //respByte, err := r.client.GetTenant(data.Name.ValueString()) respByte, err := r.client.GetTenant(data.Id.ValueString()) if err != nil { diff --git a/internal/provider/vlan_resource.go b/internal/provider/vlan_resource.go index 50b3f2f..943cf76 100644 --- a/internal/provider/vlan_resource.go +++ b/internal/provider/vlan_resource.go @@ -3,8 +3,6 @@ package provider import ( "context" "fmt" - "strconv" - "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -13,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" f5ossdk "gitswarm.f5net.com/terraform-providers/f5osclient" + "strconv" ) // Ensure provider defined types fully satisfy framework interfaces. @@ -25,7 +24,8 @@ func NewVlanResource() resource.Resource { // VlanResource defines the resource implementation. type VlanResource struct { - client *f5ossdk.F5os + client *f5ossdk.F5os + teemData *TeemData } type VlanResourceModel struct { @@ -65,6 +65,10 @@ func (r *VlanResource) Schema(ctx context.Context, req resource.SchemaRequest, r func (r *VlanResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { r.client, resp.Diagnostics = toF5osProvider(req.ProviderData) + //teemData := &TeemData{} + teemData.ProviderName = "f5os" + teemData.ResourceName = "f5os_vlan" + r.teemData = teemData } func (r *VlanResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -85,12 +89,17 @@ func (r *VlanResource) Create(ctx context.Context, req resource.CreateRequest, r tflog.Debug(ctx, fmt.Sprintf("vlanReqConfig Data:%+v", vlanReqConfig)) + teemInfo := make(map[string]interface{}) + teemInfo["teemData"] = r.teemData + err := r.client.SendTeem(teemInfo) + if err != nil { + resp.Diagnostics.AddError("Teem Error", fmt.Sprintf("Sending Teem Data failed: %s", err)) + } respByte, err := r.client.VlanConfig(vlanReqConfig) if err != nil { resp.Diagnostics.AddError("F5OS Client Error:", fmt.Sprintf("Create Vlan failed, got error: %s", err)) return } - tflog.Debug(ctx, fmt.Sprintf("vlanReqConfig Response:%+v", string(respByte))) data.Id = types.StringValue(fmt.Sprintf("%d", int(data.VlanId.ValueInt64()))) diff --git a/vendor/github.com/google/uuid/CHANGELOG.md b/vendor/github.com/google/uuid/CHANGELOG.md new file mode 100644 index 0000000..2bd7866 --- /dev/null +++ b/vendor/github.com/google/uuid/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18) + + +### Bug Fixes + +* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0)) + +## Changelog diff --git a/vendor/github.com/google/uuid/CONTRIBUTING.md b/vendor/github.com/google/uuid/CONTRIBUTING.md index 04fdf09..5566888 100644 --- a/vendor/github.com/google/uuid/CONTRIBUTING.md +++ b/vendor/github.com/google/uuid/CONTRIBUTING.md @@ -2,6 +2,22 @@ We definitely welcome patches and contribution to this project! +### Tips + +Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org). + +Always try to include a test case! If it is not possible or not necessary, +please explain why in the pull request description. + +### Releasing + +Commits that would precipitate a SemVer change, as desrcibed in the Conventional +Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action) +to create a release candidate pull request. Once submitted, `release-please` +will create a release. + +For tips on how to work with `release-please`, see its documentation. + ### Legal requirements In order to protect both you and ourselves, you will need to sign the diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md index f765a46..3e9a618 100644 --- a/vendor/github.com/google/uuid/README.md +++ b/vendor/github.com/google/uuid/README.md @@ -1,6 +1,6 @@ -# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) +# uuid The uuid package generates and inspects UUIDs based on -[RFC 4122](http://tools.ietf.org/html/rfc4122) +[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) and DCE 1.1: Authentication and Security Services. This package is based on the github.com/pborman/uuid package (previously named @@ -9,10 +9,12 @@ a UUID is a 16 byte array rather than a byte slice. One loss due to this change is the ability to represent an invalid UUID (vs a NIL UUID). ###### Install -`go get github.com/google/uuid` +```sh +go get github.com/google/uuid +``` ###### Documentation -[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) +[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid) Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here: diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go index 24b78ed..b2a0bc8 100644 --- a/vendor/github.com/google/uuid/node_js.go +++ b/vendor/github.com/google/uuid/node_js.go @@ -7,6 +7,6 @@ package uuid // getHardwareInterface returns nil values for the JS version of the code. -// This remvoves the "net" dependency, because it is not used in the browser. +// This removes the "net" dependency, because it is not used in the browser. // Using the "net" library inflates the size of the transpiled JS code by 673k bytes. func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go index a57207a..a56138c 100644 --- a/vendor/github.com/google/uuid/uuid.go +++ b/vendor/github.com/google/uuid/uuid.go @@ -69,7 +69,7 @@ func Parse(s string) (UUID, error) { // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: - if strings.ToLower(s[:9]) != "urn:uuid:" { + if !strings.EqualFold(s[:9], "urn:uuid:") { return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) } s = s[9:] @@ -101,7 +101,8 @@ func Parse(s string) (UUID, error) { 9, 11, 14, 16, 19, 21, - 24, 26, 28, 30, 32, 34} { + 24, 26, 28, 30, 32, 34, + } { v, ok := xtob(s[x], s[x+1]) if !ok { return uuid, errors.New("invalid UUID format") @@ -117,7 +118,7 @@ func ParseBytes(b []byte) (UUID, error) { switch len(b) { case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { + if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) { return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) } b = b[9:] @@ -145,7 +146,8 @@ func ParseBytes(b []byte) (UUID, error) { 9, 11, 14, 16, 19, 21, - 24, 26, 28, 30, 32, 34} { + 24, 26, 28, 30, 32, 34, + } { v, ok := xtob(b[x], b[x+1]) if !ok { return uuid, errors.New("invalid UUID format") diff --git a/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/f5os.go b/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/f5os.go index 8a3f139..e7ea339 100644 --- a/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/f5os.go +++ b/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/f5os.go @@ -66,11 +66,13 @@ type F5os struct { Token string // if set, will be used instead of User/Password Transport *http.Transport // UserAgent is an optional field that specifies the caller of this request. - UserAgent string - Teem bool - ConfigOptions *ConfigOptions - PlatformType string - UriRoot string + UserAgent string + Teem bool + ConfigOptions *ConfigOptions + PlatformType string + Metadata interface{} + PlatformVersion string + UriRoot string } type F5osError struct { IetfRestconfErrors struct { @@ -183,8 +185,8 @@ func NewSession(f5osObj *F5osConfig) (*F5os, error) { return nil, err } defer res.Body.Close() - f5osSession.Token = res.Header.Get("X-Auth-Token") respData, err := io.ReadAll(res.Body) + f5osLogger.Debug("[NewSession]", "Status Code:", hclog.Fmt("%+v", res.StatusCode)) if res.StatusCode == 401 { return nil, fmt.Errorf("%+v with error:%+v", res.Status, string(respData)) } @@ -194,7 +196,8 @@ func NewSession(f5osObj *F5osConfig) (*F5os, error) { if strings.Contains(fmt.Sprintf("%s", string(respData)), "enable JavaScript to run this app") { return nil, fmt.Errorf("Failed with %s", string(respData)) } - f5osSession.setPlaformType() + f5osSession.Token = res.Header.Get("X-Auth-Token") + f5osSession.setPlatformType() f5osLogger.Info("[NewSession] Session creation Success") return f5osSession, nil } @@ -240,6 +243,37 @@ func (p *F5os) doRequest(op, path string, body []byte) ([]byte, error) { return nil, nil } +func (p *F5os) SendTeem(teemDataInput interface{}) error { + recordData := &RawTelemetry{} + teemData := teemDataInput.(map[string]interface{})["teemData"] + teemBytes, _ := json.Marshal(teemData) + teemMap := make(map[string]interface{}) + err := json.Unmarshal(teemBytes, &teemMap) + if err != nil { + return err + } + telemetryInputs := make(map[string]interface{}) + telemetryInputs["RunningInDocker"] = inDocker() + telemetryInputs["F5Platform"] = teemMap["F5Platform"].(string) + telemetryInputs["F5SoftwareVersion"] = teemMap["F5SoftwareVersion"].(string) + telemetryInputs["ProviderName"] = teemMap["ProviderName"].(string) + telemetryInputs["ProviderVersion"] = teemMap["ProviderVersion"].(string) + telemetryInputs["ResourceName"] = teemMap["ResourceName"].(string) + telemetryInputs["TerraformLicense"] = teemMap["TerraformLicense"].(string) + telemetryInputs["TerraformVersion"] = teemMap["TerraformVersion"].(string) + recordData.TelemetryRecords = append(recordData.TelemetryRecords, telemetryInputs) + recordData.DigitalAssetName = "terraform-provider-f5os" + recordData.DigitalAssetVersion = teemMap["ProviderVersion"].(string) + recordData.DocumentType = teemMap["ResourceName"].(string) + recordData.DocumentVersion = teemMap["ProviderVersion"].(string) + recordData.ObservationStartTime = time.Now().UTC().Format(time.RFC3339Nano) + recordData.EpochTime = time.Now().Unix() + if !p.Teem { + return SendReport(recordData) + } + return nil +} + func (p *F5os) GetRequest(path string) ([]byte, error) { url := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, path) f5osLogger.Info("[GetRequest]", "Request path", hclog.Fmt("%+v", url)) @@ -276,7 +310,7 @@ func (p *F5os) PostRequest(path string, body []byte) ([]byte, error) { } func (p *F5os) GetInterface(intf string) (*F5RespOpenconfigInterface, error) { - intfnew := fmt.Sprintf("/interface=%s", intf) + intfnew := fmt.Sprintf("/interface=%s", encodeUrl(intf)) url := fmt.Sprintf("%s%s", uriInterface, intfnew) f5osLogger.Info("[GetInterface]", "Request path", hclog.Fmt("%+v", url)) intFace := &F5RespOpenconfigInterface{} @@ -289,9 +323,15 @@ func (p *F5os) GetInterface(intf string) (*F5RespOpenconfigInterface, error) { return intFace, nil } +func encodeUrl(intfname string) string { + // Encode the interface name + interfaceEncoded := url.QueryEscape(intfname) + return interfaceEncoded +} + func (p *F5os) UpdateInterface(intf string, body *F5ReqOpenconfigInterface) ([]byte, error) { f5osLogger.Debug("[UpdateInterface]", "Request path", hclog.Fmt("%+v", uriInterface)) - vlans, err := p.getSwitchedVlans(intf) + vlans, err := p.getSwitchedVlans(encodeUrl(intf)) if err != nil { return []byte(""), err } @@ -335,7 +375,7 @@ func (p *F5os) getSwitchedVlans(intf string) (*F5ReqVlanSwitchedVlan, error) { } func (p *F5os) RemoveNativeVlans(intf string) error { - intfnew := fmt.Sprintf("/interface=%s/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/openconfig-vlan:config/openconfig-vlan:native-vlan", intf) + intfnew := fmt.Sprintf("/interface=%s/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/openconfig-vlan:config/openconfig-vlan:native-vlan", encodeUrl(intf)) url := fmt.Sprintf("%s%s", uriInterface, intfnew) f5osLogger.Debug("[RemoveNativeVlans]", "Request path", hclog.Fmt("%+v", url)) err := p.DeleteRequest(url) @@ -346,7 +386,7 @@ func (p *F5os) RemoveNativeVlans(intf string) error { } func (p *F5os) RemoveTrunkVlans(intf string, vlanId int) error { - intfnew := fmt.Sprintf("/interface=%s/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/openconfig-vlan:config/openconfig-vlan:trunk-vlans=%d", intf, vlanId) + intfnew := fmt.Sprintf("/interface=%s/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/openconfig-vlan:config/openconfig-vlan:trunk-vlans=%d", encodeUrl(intf), vlanId) url := fmt.Sprintf("%s%s", uriInterface, intfnew) f5osLogger.Debug("[RemoveTrunkVlans]", "Request path", hclog.Fmt("%+v", url)) err := p.DeleteRequest(url) @@ -356,6 +396,163 @@ func (p *F5os) RemoveTrunkVlans(intf string, vlanId int) error { return nil } +func (p *F5os) GetLagInterface(intf string) (*F5RespLagInterfaces, error) { + intfnew := fmt.Sprintf("/interface=%s", encodeUrl(intf)) + url := fmt.Sprintf("%s%s", uriInterface, intfnew) + f5osLogger.Info("[GetLagInterface]", "Request path", hclog.Fmt("%+v", url)) + intLag := &F5RespLagInterfaces{} + byteData, err := p.GetRequest(url) + if err != nil { + return nil, err + } + json.Unmarshal(byteData, intLag) + f5osLogger.Debug("[GetLagInterface]", "intLag", hclog.Fmt("%+v", intLag)) + return intLag, nil +} + +func (p *F5os) CreateLagInterface(body *F5ReqLagInterfaces, members *F5ReqLagInterfaces) ([]byte, error) { + f5osLogger.Debug("[CreateLagInterface]", "Request path", hclog.Fmt("%+v", "/")) + byteBody, err := json.Marshal(body) + if err != nil { + return byteBody, err + } + f5osLogger.Debug("[CreateLagInterface]", "Request Body", hclog.Fmt("%+v", body)) + resp, err := p.PatchRequest("/", byteBody) + if err != nil { + return resp, err + } + f5osLogger.Debug("[CreateLagInterface]", "Resp:", hclog.Fmt("%+v", string(resp))) + + resp, err = p.addLagMembers(members) + if err != nil { + return resp, err + } + return resp, nil +} + +func (p *F5os) UpdateLagInterface(intf string, body *F5ReqLagInterfaces) ([]byte, error) { + f5osLogger.Debug("[UpdateLagInterface]", "Request path", hclog.Fmt("%+v", uriInterface)) + vlans, err := p.getLagSwitchedVlans(encodeUrl(intf)) + if err != nil { + return []byte(""), err + } + nativeVlan := vlans.OpenconfigVlanSwitchedVlan.Config.NativeVlan + trunkVlans := vlans.OpenconfigVlanSwitchedVlan.Config.TrunkVlans + for _, val := range body.OpenconfigInterfacesInterfaces.Interface { + innativeVlan := val.OpenconfigIfAggregateAggregation.OpenconfigVlanSwitchedVlan.Config.NativeVlan + newTrunkvlans := val.OpenconfigIfAggregateAggregation.OpenconfigVlanSwitchedVlan.Config.TrunkVlans + diffTrunkvlans := listDifference(trunkVlans, newTrunkvlans) + if nativeVlan != 0 && innativeVlan != nativeVlan { + p.removeLagNativeVlans(intf) + } + for _, intfVal := range diffTrunkvlans { + p.removeLagTrunkVlans(intf, intfVal) + } + } + byteBody, err := json.Marshal(body) + if err != nil { + return byteBody, err + } + f5osLogger.Debug("[UpdateLagInterface]", "Request Body", hclog.Fmt("%+v", body)) + resp, err := p.PatchRequest(uriInterface, byteBody) + if err != nil { + return resp, err + } + f5osLogger.Debug("[UpdateLagInterface]", "Resp:", hclog.Fmt("%+v", string(resp))) + return resp, nil +} + +func (p *F5os) getLagSwitchedVlans(intf string) (*F5ReqVlanSwitchedVlan, error) { + intfnew := fmt.Sprintf("/interface=%s/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan", intf) + url := fmt.Sprintf("%s%s", uriInterface, intfnew) + f5osLogger.Debug("[getLagSwitchedVlans]", "Request path", hclog.Fmt("%+v", url)) + intFace := &F5ReqVlanSwitchedVlan{} + byteData, err := p.GetRequest(url) + if err != nil { + return nil, err + } + json.Unmarshal(byteData, intFace) + f5osLogger.Debug("[getLagSwitchedVlans]", "intFace", hclog.Fmt("%+v", intFace)) + return intFace, nil +} + +func (p *F5os) removeLagNativeVlans(intf string) error { + intfnew := fmt.Sprintf("/interface=%s/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan/openconfig-vlan:config/openconfig-vlan:native-vlan", intf) + url := fmt.Sprintf("%s%s", uriInterface, intfnew) + f5osLogger.Debug("[RemoveLagNativeVlans]", "Request path", hclog.Fmt("%+v", url)) + err := p.DeleteRequest(url) + if err != nil { + return err + } + return nil +} + +func (p *F5os) removeLagTrunkVlans(intf string, vlanId int) error { + intfnew := fmt.Sprintf("/interface=%s/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan/openconfig-vlan:config/openconfig-vlan:trunk-vlans=%d", intf, vlanId) + url := fmt.Sprintf("%s%s", uriInterface, intfnew) + f5osLogger.Debug("[RemoveLagTrunkVlans]", "Request path", hclog.Fmt("%+v", url)) + err := p.DeleteRequest(url) + if err != nil { + return err + } + return nil +} + +func (p *F5os) RemoveLagInterface(intf string) error { + intfnew := fmt.Sprintf("/interface=%s", intf) + url := fmt.Sprintf("%s%s", uriInterface, intfnew) + f5osLogger.Debug("[RemoveLagInterface]", "Request path", hclog.Fmt("%+v", url)) + err := p.DeleteRequest(url) + if err != nil { + return err + } + return nil +} + +func (p *F5os) UpdateLagMembers(members *F5ReqLagInterfaces) ([]byte, error) { + resp, err := p.addLagMembers(members) + if err != nil { + return resp, err + } + return resp, nil +} + +func (p *F5os) addLagMembers(body *F5ReqLagInterfaces) ([]byte, error) { + f5osLogger.Debug("[UpdateLagMember]", "Request path", hclog.Fmt("%+v", "/")) + byteBody, err := json.Marshal(body) + if err != nil { + return byteBody, err + } + f5osLogger.Debug("[UpdateLagMember]", "Request Body", hclog.Fmt("%+v", body)) + resp, err := p.PatchRequest("/", byteBody) + if err != nil { + return resp, err + } + f5osLogger.Debug("[UpdateLagMember]", "Resp:", hclog.Fmt("%+v", string(resp))) + return resp, nil +} + +func (p *F5os) RemoveLagMembers(members []string) error { + for _, member := range members { + err := p.removeLagMember(member) + if err != nil { + return err + } + } + return nil +} + +func (p *F5os) removeLagMember(intf string) error { + intfnew := fmt.Sprintf("/interface=%s/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id", encodeInterface(intf)) + url := fmt.Sprintf("%s%s", uriInterface, intfnew) + f5osLogger.Debug("[RemoveLagMember]", "Request path", hclog.Fmt("%+v", url)) + err := p.DeleteRequest(url) + if err != nil { + return err + } + return nil +} + func (p *F5os) UploadImagePostRequest(path string, formData io.Reader, headers map[string]string) ([]byte, error) { url := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, path) req, err := http.NewRequest( @@ -541,9 +738,10 @@ func (p *F5os) fileTransferStatus(key, transferId string) (string, error) { return "", fmt.Errorf("no transfer status available for the file/operation-id: %s", transferId) } -func (p *F5os) setPlaformType() ([]byte, error) { - url := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, uriPlatformType) - f5osLogger.Debug("[setPlaformType]", "Request path", hclog.Fmt("%+v", url)) +func (p *F5os) setPlatformType() ([]byte, error) { + //url := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, uriPlatformType) + url := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, "/openconfig-platform:components/component") + f5osLogger.Info("[setPlatformType]", "Request path", hclog.Fmt("%+v", url)) req, err := http.NewRequest("GET", url, bytes.NewBuffer(nil)) if err != nil { return nil, err @@ -560,36 +758,184 @@ func (p *F5os) setPlaformType() ([]byte, error) { } defer resp.Body.Close() if resp.StatusCode == 200 { - p.PlatformType = "rSeries Platform" + bytes01, _ := io.ReadAll(resp.Body) + var mymap map[string]interface{} + json.Unmarshal(bytes01, &mymap) + if len(mymap["openconfig-platform:component"].([]interface{})) > 1 { + for _, val := range mymap["openconfig-platform:component"].([]interface{}) { + if val.(map[string]interface{})["name"] == "platform" { + //check state key present in above response map object + if val.(map[string]interface{})["state"].(map[string]interface{})["description"] != nil { + p.PlatformType = "rSeries Platform" + uriPlatformVersion := "/openconfig-system:system/f5-system-image:image/state/install" + p.setPlatformVersion(uriPlatformVersion) + } + } + if val.(map[string]interface{})["name"] == "chassis" { + //check state key present in above response map object + if val.(map[string]interface{})["state"].(map[string]interface{})["description"] != nil { + p.PlatformType = "Velos Controller" + uriPlatformVersion := "/openconfig-system:system/f5-system-controller-image:image" + p.setChassisVersion(uriPlatformVersion) + } + } + } + } else if len(mymap["openconfig-platform:component"].([]interface{})) == 1 { + p.PlatformType = "Velos Partition" + software, ok := mymap["openconfig-platform:component"].([]interface{})[0].(map[string]interface{})["f5-platform:software"] + if ok { + version := software.(map[string]interface{})["state"].(map[string]interface{})["software-components"].(map[string]interface{})["software-component"].([]interface{})[0].(map[string]interface{})["state"].(map[string]interface{})["version"] + softwareIndex := software.(map[string]interface{})["state"].(map[string]interface{})["software-components"].(map[string]interface{})["software-component"].([]interface{})[0].(map[string]interface{})["software-index"] + // check if software-index is blade-os then set platform version as version + if softwareIndex.(string) == "blade-os" { + p.PlatformVersion = version.(string) + platMap := make(map[string]interface{}) + platMap["PlatformVersion"] = version.(string) + p.Metadata = platMap + //append(p.Metadata, platMap) + } + } + } + f5osLogger.Debug("[setPlatformType]", "Config:", hclog.Fmt("%+v", p)) return io.ReadAll(resp.Body) } + //if resp.StatusCode == 404 { + // url1 := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, uriVlan) + // req, err := http.NewRequest("GET", url1, bytes.NewBuffer(nil)) + // if err != nil { + // return nil, err + // } + // req.Header.Set("X-Auth-Token", p.Token) + // req.Header.Set("Content-Type", contentTypeHeader) + // client := &http.Client{ + // Transport: p.Transport, + // Timeout: p.ConfigOptions.APICallTimeout, + // } + // resp, err := client.Do(req) + // if err != nil { + // return nil, err + // } + // defer resp.Body.Close() + // if resp.StatusCode == 200 || resp.StatusCode == 204 { + // p.PlatformType = "Velos Partition" + // } + // if resp.StatusCode == 404 { + // bytes, _ := io.ReadAll(resp.Body) + // var mymap map[string]interface{} + // json.Unmarshal(bytes, &mymap) + // intfVal := mymap["ietf-restconf:errors"].(map[string]interface{})["error"].([]interface{})[0].(map[string]interface{})["error-message"] + // if intfVal == "uri keypath not found" { + // p.PlatformType = "Velos Controller" + // } + // } + //} + return nil, nil +} + +// https:///api/data/openconfig-system:system/f5-system-image:image/state/install +// create setplatformVersion using above url +func (p *F5os) setPlatformVersion(uriPlatformVersion string) ([]byte, error) { + url := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, uriPlatformVersion) + // create get call for above url + f5osLogger.Debug("[SetPlatformVersion]", "Request path", hclog.Fmt("%+v", url)) + req, err := http.NewRequest("GET", url, bytes.NewBuffer(nil)) + if err != nil { + return nil, err + } + req.Header.Set("X-Auth-Token", p.Token) + req.Header.Set("Content-Type", contentTypeHeader) + client := &http.Client{ + Transport: p.Transport, + Timeout: p.ConfigOptions.APICallTimeout, + } + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() if resp.StatusCode == 404 { - url1 := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, uriVlan) - req, err := http.NewRequest("GET", url1, bytes.NewBuffer(nil)) - if err != nil { - return nil, err - } - req.Header.Set("X-Auth-Token", p.Token) - req.Header.Set("Content-Type", contentTypeHeader) - client := &http.Client{ - Transport: p.Transport, - Timeout: p.ConfigOptions.APICallTimeout, - } - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - if resp.StatusCode == 200 || resp.StatusCode == 204 { - p.PlatformType = "Velos Partition" + return nil, fmt.Errorf("Platform version not supported") + } + if resp.StatusCode == 200 || resp.StatusCode == 304 { + bytes, _ := io.ReadAll(resp.Body) + var mymap map[string]interface{} + json.Unmarshal(bytes, &mymap) + // { + // "f5-system-image:install": { + // "install-os-version": "1.7.0-3518", + // "install-service-version": "1.7.0-3518", + // "install-status": "success" + // } + // } + if mymap["f5-system-image:install"].(map[string]interface{})["install-status"] == "success" { + p.PlatformVersion = mymap["f5-system-image:install"].(map[string]interface{})["install-os-version"].(string) + platMap := make(map[string]interface{}) + platMap["PlatformVersion"] = mymap["f5-system-image:install"].(map[string]interface{})["install-os-version"].(string) + p.Metadata = platMap + //append(p.Metadata, platMap) } - if resp.StatusCode == 404 { - bytes, _ := io.ReadAll(resp.Body) - var mymap map[string]interface{} - json.Unmarshal(bytes, &mymap) - intfVal := mymap["ietf-restconf:errors"].(map[string]interface{})["error"].([]interface{})[0].(map[string]interface{})["error-message"] - if intfVal == "uri keypath not found" { - p.PlatformType = "Velos Controller" + } + return nil, nil +} + +// https:///api/data/openconfig-system:system/f5-system-controller-image:image +// create setplatformVersion using above url +func (p *F5os) setChassisVersion(uriChassisVersion string) ([]byte, error) { + url := fmt.Sprintf("%s%s%s", p.Host, p.UriRoot, uriChassisVersion) + // create get call for above url + f5osLogger.Debug("[setChassisVersion]", "Request path", hclog.Fmt("%+v", url)) + req, err := http.NewRequest("GET", url, bytes.NewBuffer(nil)) + if err != nil { + return nil, err + } + req.Header.Set("X-Auth-Token", p.Token) + req.Header.Set("Content-Type", contentTypeHeader) + client := &http.Client{ + Transport: p.Transport, + Timeout: p.ConfigOptions.APICallTimeout, + } + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode == 404 { + return nil, fmt.Errorf("Platform version not supported") + } + if resp.StatusCode == 200 || resp.StatusCode == 304 { + bytes, _ := io.ReadAll(resp.Body) + var mymap map[string]interface{} + json.Unmarshal(bytes, &mymap) + // { + // "f5-system-controller-image:image": { + // "state": { + // "controllers": { + // "controller": [ + // { + // "number": 1, + // "os-version": "1.6.0-9817", + // "service-version": "1.6.0-9817", + // "install-status": "success" + // }, + // { + // "number": 2, + // "os-version": "1.6.0-9817", + // "service-version": "1.6.0-9817", + // "install-status": "success" + // } + // ] + // } + // } + // } + // } + // check if install-status is success for all controllers + for _, val := range mymap["f5-system-controller-image:image"].(map[string]interface{})["state"].(map[string]interface{})["controllers"].(map[string]interface{})["controller"].([]interface{}) { + if val.(map[string]interface{})["install-status"] == "success" { + p.PlatformVersion = val.(map[string]interface{})["os-version"].(string) + platMap := make(map[string]interface{}) + platMap["PlatformVersion"] = val.(map[string]interface{})["os-version"].(string) + p.Metadata = platMap + //append(p.Metadata, platMap) } } } @@ -624,3 +970,9 @@ func listDifference(s1 []int, s2 []int) []int { } return difference } + +func encodeInterface(intfname string) string { + // Encode the interface name + interfaceEncoded := url.QueryEscape(intfname) + return interfaceEncoded +} diff --git a/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/structs_partition.go b/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/structs_partition.go index b2d0f5d..d22b1e4 100644 --- a/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/structs_partition.go +++ b/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/structs_partition.go @@ -163,15 +163,85 @@ type F5ReqVlanSwitchedVlan struct { } `json:"openconfig-vlan:switched-vlan,omitempty"` } -//type F5osInterfaces struct { -// OpenconfigInterfacesInterfaces []OpenconfigInterfacesInterface `json:"openconfig-interfaces:interface,omitempty"` -//} -// -//type F5RespOpenconfigInterface struct { -// OpenconfigInterfacesInterfaces struct { -// Interface []F5RespInterface `json:"interface,omitempty"` -// } `json:"openconfig-interfaces:interfaces,omitempty"` -//} +type F5ReqLagInterfaces struct { + OpenconfigInterfacesInterfaces struct { + Interface []F5ReqLagInterface `json:"interface,omitempty"` + } `json:"openconfig-interfaces:interfaces,omitempty"` +} + +type F5ReqLagInterface struct { + Name string `json:"name,omitempty"` + Config struct { + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Enabled bool `json:"enabled,omitempty"` + } `json:"config,omitempty"` + OpenconfigIfAggregateAggregation struct { + OpenconfigVlanSwitchedVlan struct { + Config struct { + NativeVlan int `json:"native-vlan,omitempty"` + TrunkVlans []int `json:"trunk-vlans,omitempty"` + } `json:"config,omitempty"` + } `json:"openconfig-vlan:switched-vlan,omitempty"` + Config struct { + LagType string `json:"lag-type,omitempty"` + DistributioHash string `json:"f5-if-aggregate:distribution-hash,omitempty"` + } `json:"config,omitempty"` + } `json:"openconfig-if-aggregate:aggregation,omitempty"` + OpenconfigIfEthernetEthernet struct { + Config struct { + Name string `json:"openconfig-if-aggregate:aggregate-id,omitempty"` + } `json:"config,omitempty"` + } `json:"openconfig-if-ethernet:ethernet,omitempty"` +} + +type F5RespLagInterfaces struct { + OpenconfigInterfacesInterface []F5RespLagInterface `json:"openconfig-interfaces:interface,omitempty"` +} + +type F5RespLagInterface struct { + Name string `json:"name,omitempty"` + Config struct { + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Description string `json:"description,omitempty"` + Enabled bool `json:"enabled,omitempty"` + } `json:"config,omitempty"` + State struct { + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Mtu int `json:"mtu,omitempty"` + Enabled bool `json:"enabled,omitempty"` + OperStatus string `json:"oper-status,omitempty"` + } `json:"state,omitempty"` + OpenconfigIfAggregateAggregation struct { + Config struct { + LagType string `json:"lag-type,omitempty"` + DistributioHash string `json:"f5-if-aggregate:distribution-hash,omitempty"` + } `json:"config,omitempty"` + State struct { + LagType string `json:"lag-type,omitempty"` + LagSpeed int `json:"lag-speed,omitempty"` + DistributioHash string `json:"f5-if-aggregate:distribution-hash,omitempty"` + Members struct { + Member []F5RespLagMembers `json:"member,omitempty"` + } `json:"f5-if-aggregate:members,omitempty"` + MacAddress string `json:"f5-if-aggregate:mac-address,omitempty"` + LagId int `json:"f5-if-aggregate:lagid,omitempty"` + } `json:"state,omitempty"` + OpenconfigVlanSwitchedVlan struct { + Config struct { + NativeVlan int `json:"native-vlan,omitempty"` + TrunkVlans []int `json:"trunk-vlans,omitempty"` + } `json:"config,omitempty"` + } `json:"openconfig-vlan:switched-vlan,omitempty"` + } `json:"openconfig-if-aggregate:aggregation,omitempty"` +} + +type F5RespLagMembers struct { + Name string `json:"member-name,omitempty"` + Status string `json:"member-status,omitempty"` +} type F5RespOpenconfigInterface struct { OpenconfigInterfacesInterface []F5RespInterface `json:"openconfig-interfaces:interface,omitempty"` diff --git a/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/teemclient.go b/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/teemclient.go new file mode 100644 index 0000000..93ad4ef --- /dev/null +++ b/vendor/gitswarm.f5net.com/terraform-providers/f5osclient/teemclient.go @@ -0,0 +1,100 @@ +package f5os + +import ( + "bytes" + "crypto/md5" + "crypto/tls" + "encoding/json" + "fmt" + uuid "github.com/google/uuid" + "github.com/hashicorp/go-hclog" + "io" + "net/http" + "os" + "time" +) + +const ( + productName string = "terraform-provider-f5os" + prodEndPoint string = "product.apis.f5.com" + testEndpoint string = "product-stg.apis.f5networks.net" //"product-tst.apis.f5networks.net" + ProdKey string = "mmhJU2sCd63BznXAXDh4kxLIyfIMm3Ar" + testKey string = "bWMssM43DzDTX1bXA9CVzdKkOIrk1I8Z" +) + +type RawTelemetry struct { + DigitalAssetId string `json:"digitalAssetId,omitempty"` + DigitalAssetName string `json:"digitalAssetName,omitempty"` + DigitalAssetVersion string `json:"digitalAssetVersion,omitempty"` + DocumentType string `json:"documentType,omitempty"` + DocumentVersion string `json:"documentVersion,omitempty"` + EpochTime int64 `json:"epochTime,omitempty"` + ObservationEndTime string `json:"observationEndTime,omitempty"` + ObservationStartTime string `json:"observationStartTime,omitempty"` + TelemetryRecords []map[string]interface{} `json:"telemetryRecords,omitempty"` +} + +func SendReport(telemetryRecords *RawTelemetry) error { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} + client := &http.Client{Transport: tr} + url := fmt.Sprintf("https://%s/ee/v1/telemetry", prodEndPoint) + //url := fmt.Sprintf("https://%s/ee/v1/telemetry", testEndpoint) + uniqueID := uniqueUUID() + telemetryRecords.DigitalAssetId = uniqueID + telemetryRecords.ObservationEndTime = time.Now().UTC().Format(time.RFC3339Nano) + bodyInfo, _ := json.Marshal(telemetryRecords) + body := bytes.NewReader([]byte(bodyInfo)) + req, err := http.NewRequest("POST", url, body) + if err != nil { + fmt.Printf("Error found:%v", err) + } + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("F5-ApiKey", ProdKey) + //req.Header.Set("F5-ApiKey", testKey) + req.Header.Set("F5-DigitalAssetId", uniqueID) + req.Header.Set("F5-TraceId", genUUID()) + f5osLogger.Debug("[SendReport]", "Req :", hclog.Fmt("%+v", req)) + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("telemetry request to teem server failed with :%v", err) + } + f5osLogger.Debug("[SendReport]", "", hclog.Fmt("Resp Code:%+v \t Status:%+v", resp.StatusCode, resp.Status)) + defer resp.Body.Close() + data, _ := io.ReadAll(resp.Body) + if resp.StatusCode != 204 { + return fmt.Errorf("telemetry request to teem server failed with:%v", string(data[:])) + } + return nil +} + +func inDocker() bool { + if _, err := os.Stat("/.dockerenv"); err == nil { + return true + } + return false +} + +func genUUID() string { + id := uuid.New() + return id.String() +} + +var osHostname = os.Hostname + +func uniqueUUID() string { + hostname, err := osHostname() + hash := md5.New() + if err != nil { + return genUUID() + } + _, _ = io.WriteString(hash, hostname) + seed := hash.Sum(nil) + uid, err := uuid.FromBytes(seed[0:16]) + if err != nil { + return genUUID() + } + result := uid.String() + return result +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2aed146..ffbb1ac 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -41,7 +41,7 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/uuid v1.3.0 +# github.com/google/uuid v1.3.1 ## explicit github.com/google/uuid # github.com/hashicorp/errwrap v1.1.0 @@ -307,7 +307,7 @@ github.com/zclconf/go-cty/cty/function/stdlib github.com/zclconf/go-cty/cty/gocty github.com/zclconf/go-cty/cty/json github.com/zclconf/go-cty/cty/set -# gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2-0.20230817082623-065c38e0fe3e +# gitswarm.f5net.com/terraform-providers/f5osclient v1.0.2 ## explicit; go 1.19 gitswarm.f5net.com/terraform-providers/f5osclient # golang.org/x/crypto v0.7.0