diff --git a/cmd/ilx_example/example.go b/cmd/ilx_example/example.go new file mode 100644 index 0000000..2be9f2a --- /dev/null +++ b/cmd/ilx_example/example.go @@ -0,0 +1,107 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/f5devcentral/go-bigip" +) + +func main() { + // Connect to the BIG-IP system. + config := bigip.Config{ + Address: os.Getenv("BIG_IP_HOST"), + Username: os.Getenv("BIG_IP_USER"), + Password: os.Getenv("BIG_IP_PASSWORD"), + CertVerifyDisable: true, + } + + // Create a new BIG-IP session with the provided configuration. + f5 := bigip.NewSession(&config) + + // Create a context with a timeout of 1 second. + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + // Define the workspace configuration. + workspaceConfig := bigip.WorkspaceConfig{ + WorkspaceName: "ExampleWorkspace", + Partition: "Common", + } + + // Create a new workspace. + err := f5.CreateWorkspace(ctx, workspaceConfig.WorkspaceName) + if err != nil { + panic(err) + } + + // Fetch the details of the created workspace. + result, err := f5.GetWorkspace(ctx, workspaceConfig.WorkspaceName) + if err != nil { + panic(err) + } + log.Printf("Workspace: %v", result) + + // Define the extension configuration. + opts := bigip.ExtensionConfig{ + WorkspaceConfig: workspaceConfig, + ExtensionName: "exampleExt", + } + + // Create a new extension. + err = f5.CreateExtension(ctx, opts) + if err != nil { + panic(err) + } + + // Read the package.json file. + const packagePath string = "cmd/ilx_example/ilx/package.json" + packagejson, err := os.ReadFile(packagePath) + if err != nil { + fmt.Println("File reading error", err) + return + } + + // Write the package.json file to the extension. + err = f5.WriteExtensionFile(ctx, opts, string(packagejson), bigip.PackageJSON) + if err != nil { + panic(err) + } + + // Read the index.js file. + const indexjsPath string = "cmd/ilx_example/ilx/index.js" + indexjs, err := os.ReadFile(indexjsPath) + if err != nil { + fmt.Println("File reading error", err) + return + } + + // Write the index.js file to the extension. + err = f5.WriteExtensionFile(ctx, opts, string(indexjs), bigip.IndexJS) + if err != nil { + panic(err) + } + + // Read the index.js file from the extension. + content, err := f5.ReadExtensionFile(ctx, opts, bigip.IndexJS) + if err != nil { + panic(err) + } + fmt.Printf("Content: %+v\n", content) + + // Write a TCL rule file to the workspace. + err = f5.WriteRuleFile(ctx, workspaceConfig, "", "Example.tcl") + if err != nil { + panic(err) + } + + // Read the TCL rule file from the workspace. + out, err := f5.ReadRuleFile(ctx, workspaceConfig, "Example.tcl") + if err != nil { + panic(err) + } + log.Printf("Rule: %v", out.Content) +} diff --git a/cmd/ilx_example/ilx/index.js b/cmd/ilx_example/ilx/index.js new file mode 100644 index 0000000..f891109 --- /dev/null +++ b/cmd/ilx_example/ilx/index.js @@ -0,0 +1,10 @@ +var f5 = require('f5-nodejs'); +var ilx = new f5.ILXServer(); + +ilx.addMethod('greeter', function(req, res) { + var arg = req.params()[0]; + + res.reply('Hello ' + arg); +}); + +ilx.listen(); diff --git a/cmd/ilx_example/ilx/package-lock.json b/cmd/ilx_example/ilx/package-lock.json new file mode 100644 index 0000000..173153a --- /dev/null +++ b/cmd/ilx_example/ilx/package-lock.json @@ -0,0 +1,34 @@ +{ + "name": "ilx", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ilx", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "f5-nodejs": "^1.0.0", + "hexy": "^0.3.5" + } + }, + "node_modules/f5-nodejs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/f5-nodejs/-/f5-nodejs-1.0.0.tgz", + "integrity": "sha512-NUTwNOKVMqyk65ba4xkabJx77FvEZRJt3gpQjtNOfvEFxKKAHFAhfcgCY8xiP8fL3Iua4so1EJKsMxhVJk5OUg==" + }, + "node_modules/hexy": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.3.5.tgz", + "integrity": "sha512-UCP7TIZPXz5kxYJnNOym+9xaenxCLor/JyhKieo8y8/bJWunGh9xbhy3YrgYJUQ87WwfXGm05X330DszOfINZw==", + "license": "MIT", + "bin": { + "hexy": "bin/hexy_cmd.js" + }, + "engines": { + "node": ">=10.4" + } + } + } +} diff --git a/cmd/ilx_example/ilx/package.json b/cmd/ilx_example/ilx/package.json new file mode 100644 index 0000000..8b8a168 --- /dev/null +++ b/cmd/ilx_example/ilx/package.json @@ -0,0 +1,15 @@ +{ + "name": "ilx", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "npm run dev" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "f5-nodejs": "^1.0.0", + "hexy": "^0.3.5" + } +} diff --git a/device.go b/device.go index d712e13..09fa6dd 100644 --- a/device.go +++ b/device.go @@ -185,19 +185,21 @@ func (p *Devicegroup) UnmarshalJSON(b []byte) error { // https://10.192.74.80/mgmt/cm/device/licensing/pool/purchased-pool/licenses // The above command will spit out license uuid and which should be mapped uriUuid const ( - uriMgmt = "mgmt" - uriCm = "cm" - uriDiv = "device" - uriDevices = "devices" - uriDG = "device-group" - uriLins = "licensing" - uriPoo = "pool" - uriPur = "purchased-pool" - uriLicn = "licenses" - uriMemb = "members" - uriUtility = "utility" - uriOfferings = "offerings" - uriF5BIGMSPBT10G = "f37c66e0-a80d-43e8-924b-3bbe9fe96bbe" + uriMgmt = "mgmt" + uriCm = "cm" + uriDiv = "device" + uriDevices = "devices" + uriDG = "device-group" + uriLins = "licensing" + uriPoo = "pool" + uriPur = "purchased-pool" + uriLicn = "licenses" + uriMemb = "members" + uriUtility = "utility" + uriOfferings = "offerings" + uriF5BIGMSPBT10G = "f37c66e0-a80d-43e8-924b-3bbe9fe96bbe" + uriWorkspace = "workspace" + WORKSPACE_UPLOAD_PATH = "/var/ilx/workspaces" ) func (p *LIC) MarshalJSON() ([]byte, error) { diff --git a/ilx.go b/ilx.go new file mode 100644 index 0000000..e94e79d --- /dev/null +++ b/ilx.go @@ -0,0 +1,132 @@ +package bigip + +import ( + "context" + "fmt" +) + +type ILXWorkspace struct { + Name string `json:"name,omitempty"` + FullPath string `json:"fullPath,omitempty"` + SelfLink string `json:"selfLink,omitempty"` + NodeVersion string `json:"nodeVersion,omitempty"` + StagedDirectory string `json:"stagedDirectory,omitempty"` + Version string `json:"version,omitempty"` + Extensions []Extension `json:"extensions,omitempty"` + Rules []ILXFile `json:"rules,omitempty"` + Generation int `json:"generation,omitempty"` +} + +type ILXFile struct { + Name string `json:"name,omitempty"` + Content string `json:"content,omitempty"` +} + +type Extension struct { + Name string `json:"name,omitempty"` + Files []ILXFile `json:"files,omitempty"` +} + +func (b *BigIP) GetWorkspace(ctx context.Context, path string) (*ILXWorkspace, error) { + spc := &ILXWorkspace{} + err, exists := b.getForEntity(spc, uriMgmt, uriTm, uriIlx, uriWorkspace, path) + if !exists { + return nil, fmt.Errorf("workspace does not exist: %w", err) + } + if err != nil { + return nil, fmt.Errorf("error getting ILX Workspace: %w", err) + } + + return spc, nil +} + +func (b *BigIP) CreateWorkspace(ctx context.Context, path string) error { + err := b.post(ILXWorkspace{Name: path}, uriMgmt, uriTm, uriIlx, uriWorkspace, "") + if err != nil { + return fmt.Errorf("error creating ILX Workspace: %w", err) + } + + return nil +} + +func (b *BigIP) DeleteWorkspace(ctx context.Context, name string) error { + err := b.delete(uriMgmt, uriTm, uriIlx, uriWorkspace, name) + if err != nil { + return fmt.Errorf("error deleting ILX Workspace: %w", err) + } + return nil +} + +func (b *BigIP) CreateExtension(ctx context.Context, opts ExtensionConfig) error { + err := b.post(ILXWorkspace{Name: opts.WorkspaceName}, uriMgmt, uriTm, uriIlx, uriWorkspace+"?options=extension,"+opts.ExtensionName) + if err != nil { + return fmt.Errorf("error creating ILX Extension: %w", err) + } + return nil +} + +type ExtensionFile string + +func (e ExtensionFile) Validate() error { + if e != PackageJSON && e != IndexJS { + return fmt.Errorf("invalid extension file") + } + return nil +} + +const ( + PackageJSON ExtensionFile = "package.json" + IndexJS ExtensionFile = "index.js" +) + +type WorkspaceConfig struct { + WorkspaceName string `json:"name,omitempty"` + Partition string `json:"partition,omitempty"` +} + +type ExtensionConfig struct { + WorkspaceConfig + ExtensionName string `json:"extensionName,omitempty"` +} + +func (b *BigIP) WriteRuleFile(ctx context.Context, opts WorkspaceConfig, content string, filename string) error { + destination := fmt.Sprintf("%s/%s/%s/rules/%s", WORKSPACE_UPLOAD_PATH, opts.Partition, opts.WorkspaceName, filename) + err := b.WriteFile(content, destination) + if err != nil { + return fmt.Errorf("error uploading rule file: %w", err) + } + return nil +} + +func (b *BigIP) WriteExtensionFile(ctx context.Context, opts ExtensionConfig, content string, filename ExtensionFile) error { + if err := filename.Validate(); err != nil { + return err + } + destination := fmt.Sprintf("%s/%s/%s/extensions/%s/%s", WORKSPACE_UPLOAD_PATH, opts.WorkspaceConfig.Partition, opts.WorkspaceConfig.WorkspaceName, opts.ExtensionName, filename) + err := b.WriteFile(content, destination) + if err != nil { + return fmt.Errorf("error uploading packagejson: %w", err) + } + return nil +} + +func (b *BigIP) ReadExtensionFile(ctx context.Context, opts ExtensionConfig, filename ExtensionFile) (*ILXFile, error) { + if err := filename.Validate(); err != nil { + return nil, err + } + destination := fmt.Sprintf("%s/%s/%s/extensions/%s/%s", WORKSPACE_UPLOAD_PATH, opts.Partition, opts.WorkspaceConfig.WorkspaceName, opts.ExtensionName, filename) + files, err := b.ReadFile(destination) + if err != nil { + return nil, err + } + return files, nil +} + +func (b *BigIP) ReadRuleFile(ctx context.Context, opts WorkspaceConfig, filename string) (*ILXFile, error) { + destination := fmt.Sprintf("%s/%s/%s/rules/%s", WORKSPACE_UPLOAD_PATH, opts.Partition, opts.WorkspaceName, filename) + files, err := b.ReadFile(destination) + if err != nil { + return nil, err + } + return files, nil +} diff --git a/sys.go b/sys.go index 5e39b23..33227ef 100644 --- a/sys.go +++ b/sys.go @@ -15,6 +15,7 @@ import ( "fmt" "log" "os" + "path" "strconv" //"strings" @@ -668,7 +669,6 @@ func (b *BigIP) CreateProvision(name string, fullPath string, cpuRatio int, disk } if name == "afm" { return b.put(config, uriSys, uriProvision, uriAfm) - } if name == "gtm" { return b.put(config, uriSys, uriProvision, uriGtm) @@ -834,7 +834,6 @@ func (b *BigIP) StartTransaction() (*Transaction, error) { b.Transaction = "" body := make(map[string]interface{}) resp, err := b.postReq(body, uriMgmt, uriTm, uriTransaction) - if err != nil { return nil, fmt.Errorf("error encountered while starting transaction: %v", err) } @@ -1051,7 +1050,6 @@ func (b *BigIP) GetOCSP(name string) (*OCSP, error) { } js, err := json.Marshal(ocsp) - if err != nil { return nil, fmt.Errorf("error encountered while marshalling ocsp: %v", err) } @@ -1098,3 +1096,33 @@ func (b *BigIP) ModifyFolderDescription(partition string, body map[string]string partition = fmt.Sprintf("~%s", partition) return b.patch(body, uriSys, uriFolder, partition) } + +// Write to a file using the echo command: echo "content" > destination +func (b *BigIP) WriteFile(content string, destination string) error { + cmd := BigipCommand{ + Command: "run", + UtilCmdArgs: fmt.Sprintf("-c 'echo \"%s\" > \"%s\"'", content, destination), + } + _, err := b.RunCommand(&cmd) + if err != nil { + return fmt.Errorf("error running command: %w", err) + } + return nil +} + +// Read a file using the cat command +func (b *BigIP) ReadFile(destination string) (*ILXFile, error) { + fileContentCommand := BigipCommand{ + Command: "run", + UtilCmdArgs: fmt.Sprintf("-c 'cat %s'", destination), + } + fileContent, err := b.RunCommand(&fileContentCommand) + if err != nil { + return nil, fmt.Errorf("error running command: %w", err) + } + file := &ILXFile{ + Name: path.Base(destination), + Content: fileContent.CommandResult, + } + return file, nil +}