You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pe...@apache.org on 2025/09/22 21:14:54 UTC

(cloudstack-go) branch add-extensions-api-support created (now aad483d)

This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a change to branch add-extensions-api-support
in repository https://gitbox.apache.org/repos/asf/cloudstack-go.git


      at aad483d  Add support for extension APIs

This branch includes the following new commits:

     new aad483d  Add support for extension APIs

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(cloudstack-go) 01/01: Add support for extension APIs

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch add-extensions-api-support
in repository https://gitbox.apache.org/repos/asf/cloudstack-go.git

commit aad483db1632aa9e62abaec8a36b5ca2489ee4b4
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Mon Sep 22 17:14:41 2025 -0400

    Add support for extension APIs
---
 cloudstack/ExtensionService.go      | 2540 +++++++++++++++++++++++++++++++++++
 cloudstack/ExtensionService_mock.go |  504 +++++++
 cloudstack/cloudstack.go            |   11 +
 generate/layout.go                  |   13 +
 test/ExtensionService_test.go       |  191 +++
 5 files changed, 3259 insertions(+)

diff --git a/cloudstack/ExtensionService.go b/cloudstack/ExtensionService.go
new file mode 100644
index 0000000..b565f4a
--- /dev/null
+++ b/cloudstack/ExtensionService.go
@@ -0,0 +1,2540 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package cloudstack
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/url"
+	"strconv"
+	"strings"
+)
+
+type ExtensionServiceIface interface {
+	AddCustomAction(p *AddCustomActionParams) (*AddCustomActionResponse, error)
+	NewAddCustomActionParams(extensionid string, name string) *AddCustomActionParams
+	CreateExtension(p *CreateExtensionParams) (*CreateExtensionResponse, error)
+	NewCreateExtensionParams(name string, extensionType string) *CreateExtensionParams
+	DeleteCustomAction(p *DeleteCustomActionParams) (*DeleteCustomActionResponse, error)
+	NewDeleteCustomActionParams() *DeleteCustomActionParams
+	DeleteExtension(p *DeleteExtensionParams) (*DeleteExtensionResponse, error)
+	NewDeleteExtensionParams() *DeleteExtensionParams
+	ListCustomActions(p *ListCustomActionsParams) (*ListCustomActionsResponse, error)
+	NewListCustomActionsParams() *ListCustomActionsParams
+	GetCustomActionID(name string, opts ...OptionFunc) (string, int, error)
+	GetCustomActionByName(name string, opts ...OptionFunc) (*CustomAction, int, error)
+	GetCustomActionByID(id string, opts ...OptionFunc) (*CustomAction, int, error)
+	ListExtensions(p *ListExtensionsParams) (*ListExtensionsResponse, error)
+	NewListExtensionsParams() *ListExtensionsParams
+	GetExtensionID(name string, opts ...OptionFunc) (string, int, error)
+	GetExtensionByName(name string, opts ...OptionFunc) (*Extension, int, error)
+	GetExtensionByID(id string, opts ...OptionFunc) (*Extension, int, error)
+	RegisterExtension(p *RegisterExtensionParams) (*RegisterExtensionResponse, error)
+	NewRegisterExtensionParams(extensionid string, resourceid string, resourcetype string) *RegisterExtensionParams
+	RunCustomAction(p *RunCustomActionParams) (*RunCustomActionResponse, error)
+	NewRunCustomActionParams(customactionid string, resourceid string) *RunCustomActionParams
+	UnregisterExtension(p *UnregisterExtensionParams) (*UnregisterExtensionResponse, error)
+	NewUnregisterExtensionParams(extensionid string, resourceid string, resourcetype string) *UnregisterExtensionParams
+	UpdateCustomAction(p *UpdateCustomActionParams) (*UpdateCustomActionResponse, error)
+	NewUpdateCustomActionParams(id string) *UpdateCustomActionParams
+	UpdateExtension(p *UpdateExtensionParams) (*UpdateExtensionResponse, error)
+	NewUpdateExtensionParams(id string) *UpdateExtensionParams
+}
+
+type AddCustomActionParams struct {
+	p map[string]interface{}
+}
+
+func (p *AddCustomActionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["allowedroletypes"]; found {
+		vv := strings.Join(v.([]string), ",")
+		u.Set("allowedroletypes", vv)
+	}
+	if v, found := p.p["description"]; found {
+		u.Set("description", v.(string))
+	}
+	if v, found := p.p["details"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("details[%d].%s", i, k), m[k])
+		}
+	}
+	if v, found := p.p["enabled"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("enabled", vv)
+	}
+	if v, found := p.p["errormessage"]; found {
+		u.Set("errormessage", v.(string))
+	}
+	if v, found := p.p["extensionid"]; found {
+		u.Set("extensionid", v.(string))
+	}
+	if v, found := p.p["name"]; found {
+		u.Set("name", v.(string))
+	}
+	if v, found := p.p["parameters"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("parameters[%d].key", i), k)
+			u.Set(fmt.Sprintf("parameters[%d].value", i), m[k])
+		}
+	}
+	if v, found := p.p["resourcetype"]; found {
+		u.Set("resourcetype", v.(string))
+	}
+	if v, found := p.p["successmessage"]; found {
+		u.Set("successmessage", v.(string))
+	}
+	if v, found := p.p["timeout"]; found {
+		vv := strconv.Itoa(v.(int))
+		u.Set("timeout", vv)
+	}
+	return u
+}
+
+func (p *AddCustomActionParams) SetAllowedroletypes(v []string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["allowedroletypes"] = v
+}
+
+func (p *AddCustomActionParams) ResetAllowedroletypes() {
+	if p.p != nil && p.p["allowedroletypes"] != nil {
+		delete(p.p, "allowedroletypes")
+	}
+}
+
+func (p *AddCustomActionParams) GetAllowedroletypes() ([]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["allowedroletypes"].([]string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetDescription(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["description"] = v
+}
+
+func (p *AddCustomActionParams) ResetDescription() {
+	if p.p != nil && p.p["description"] != nil {
+		delete(p.p, "description")
+	}
+}
+
+func (p *AddCustomActionParams) GetDescription() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["description"].(string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetDetails(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["details"] = v
+}
+
+func (p *AddCustomActionParams) ResetDetails() {
+	if p.p != nil && p.p["details"] != nil {
+		delete(p.p, "details")
+	}
+}
+
+func (p *AddCustomActionParams) GetDetails() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["details"].(map[string]string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetEnabled(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["enabled"] = v
+}
+
+func (p *AddCustomActionParams) ResetEnabled() {
+	if p.p != nil && p.p["enabled"] != nil {
+		delete(p.p, "enabled")
+	}
+}
+
+func (p *AddCustomActionParams) GetEnabled() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["enabled"].(bool)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetErrormessage(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["errormessage"] = v
+}
+
+func (p *AddCustomActionParams) ResetErrormessage() {
+	if p.p != nil && p.p["errormessage"] != nil {
+		delete(p.p, "errormessage")
+	}
+}
+
+func (p *AddCustomActionParams) GetErrormessage() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["errormessage"].(string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetExtensionid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["extensionid"] = v
+}
+
+func (p *AddCustomActionParams) ResetExtensionid() {
+	if p.p != nil && p.p["extensionid"] != nil {
+		delete(p.p, "extensionid")
+	}
+}
+
+func (p *AddCustomActionParams) GetExtensionid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["extensionid"].(string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetName(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["name"] = v
+}
+
+func (p *AddCustomActionParams) ResetName() {
+	if p.p != nil && p.p["name"] != nil {
+		delete(p.p, "name")
+	}
+}
+
+func (p *AddCustomActionParams) GetName() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["name"].(string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetParameters(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["parameters"] = v
+}
+
+func (p *AddCustomActionParams) ResetParameters() {
+	if p.p != nil && p.p["parameters"] != nil {
+		delete(p.p, "parameters")
+	}
+}
+
+func (p *AddCustomActionParams) GetParameters() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["parameters"].(map[string]string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetResourcetype(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourcetype"] = v
+}
+
+func (p *AddCustomActionParams) ResetResourcetype() {
+	if p.p != nil && p.p["resourcetype"] != nil {
+		delete(p.p, "resourcetype")
+	}
+}
+
+func (p *AddCustomActionParams) GetResourcetype() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourcetype"].(string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetSuccessmessage(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["successmessage"] = v
+}
+
+func (p *AddCustomActionParams) ResetSuccessmessage() {
+	if p.p != nil && p.p["successmessage"] != nil {
+		delete(p.p, "successmessage")
+	}
+}
+
+func (p *AddCustomActionParams) GetSuccessmessage() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["successmessage"].(string)
+	return value, ok
+}
+
+func (p *AddCustomActionParams) SetTimeout(v int) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["timeout"] = v
+}
+
+func (p *AddCustomActionParams) ResetTimeout() {
+	if p.p != nil && p.p["timeout"] != nil {
+		delete(p.p, "timeout")
+	}
+}
+
+func (p *AddCustomActionParams) GetTimeout() (int, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["timeout"].(int)
+	return value, ok
+}
+
+// You should always use this function to get a new AddCustomActionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewAddCustomActionParams(extensionid string, name string) *AddCustomActionParams {
+	p := &AddCustomActionParams{}
+	p.p = make(map[string]interface{})
+	p.p["extensionid"] = extensionid
+	p.p["name"] = name
+	return p
+}
+
+// Add a custom action for an extension
+func (s *ExtensionService) AddCustomAction(p *AddCustomActionParams) (*AddCustomActionResponse, error) {
+	resp, err := s.cs.newPostRequest("addCustomAction", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r AddCustomActionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type AddCustomActionResponse struct {
+	Allowedroletypes []string                            `json:"allowedroletypes"`
+	Created          string                              `json:"created"`
+	Description      string                              `json:"description"`
+	Details          map[string]string                   `json:"details"`
+	Enabled          bool                                `json:"enabled"`
+	Errormessage     string                              `json:"errormessage"`
+	Extensionid      string                              `json:"extensionid"`
+	Extensionname    string                              `json:"extensionname"`
+	Id               string                              `json:"id"`
+	JobID            string                              `json:"jobid"`
+	Jobstatus        int                                 `json:"jobstatus"`
+	Name             string                              `json:"name"`
+	Parameters       []AddCustomActionResponseParameters `json:"parameters"`
+	Resourcetype     string                              `json:"resourcetype"`
+	Successmessage   string                              `json:"successmessage"`
+	Timeout          int                                 `json:"timeout"`
+}
+
+type AddCustomActionResponseParameters struct {
+	Name             string   `json:"name"`
+	Required         bool     `json:"required"`
+	Type             string   `json:"type"`
+	Validationformat string   `json:"validationformat"`
+	Valueoptions     []string `json:"valueoptions"`
+}
+
+type CreateExtensionParams struct {
+	p map[string]interface{}
+}
+
+func (p *CreateExtensionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["description"]; found {
+		u.Set("description", v.(string))
+	}
+	if v, found := p.p["details"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("details[%d].%s", i, k), m[k])
+		}
+	}
+	if v, found := p.p["name"]; found {
+		u.Set("name", v.(string))
+	}
+	if v, found := p.p["orchestratorrequirespreparevm"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("orchestratorrequirespreparevm", vv)
+	}
+	if v, found := p.p["path"]; found {
+		u.Set("path", v.(string))
+	}
+	if v, found := p.p["state"]; found {
+		u.Set("state", v.(string))
+	}
+	if v, found := p.p["type"]; found {
+		u.Set("type", v.(string))
+	}
+	return u
+}
+
+func (p *CreateExtensionParams) SetDescription(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["description"] = v
+}
+
+func (p *CreateExtensionParams) ResetDescription() {
+	if p.p != nil && p.p["description"] != nil {
+		delete(p.p, "description")
+	}
+}
+
+func (p *CreateExtensionParams) GetDescription() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["description"].(string)
+	return value, ok
+}
+
+func (p *CreateExtensionParams) SetDetails(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["details"] = v
+}
+
+func (p *CreateExtensionParams) ResetDetails() {
+	if p.p != nil && p.p["details"] != nil {
+		delete(p.p, "details")
+	}
+}
+
+func (p *CreateExtensionParams) GetDetails() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["details"].(map[string]string)
+	return value, ok
+}
+
+func (p *CreateExtensionParams) SetName(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["name"] = v
+}
+
+func (p *CreateExtensionParams) ResetName() {
+	if p.p != nil && p.p["name"] != nil {
+		delete(p.p, "name")
+	}
+}
+
+func (p *CreateExtensionParams) GetName() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["name"].(string)
+	return value, ok
+}
+
+func (p *CreateExtensionParams) SetOrchestratorrequirespreparevm(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["orchestratorrequirespreparevm"] = v
+}
+
+func (p *CreateExtensionParams) ResetOrchestratorrequirespreparevm() {
+	if p.p != nil && p.p["orchestratorrequirespreparevm"] != nil {
+		delete(p.p, "orchestratorrequirespreparevm")
+	}
+}
+
+func (p *CreateExtensionParams) GetOrchestratorrequirespreparevm() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["orchestratorrequirespreparevm"].(bool)
+	return value, ok
+}
+
+func (p *CreateExtensionParams) SetPath(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["path"] = v
+}
+
+func (p *CreateExtensionParams) ResetPath() {
+	if p.p != nil && p.p["path"] != nil {
+		delete(p.p, "path")
+	}
+}
+
+func (p *CreateExtensionParams) GetPath() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["path"].(string)
+	return value, ok
+}
+
+func (p *CreateExtensionParams) SetState(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["state"] = v
+}
+
+func (p *CreateExtensionParams) ResetState() {
+	if p.p != nil && p.p["state"] != nil {
+		delete(p.p, "state")
+	}
+}
+
+func (p *CreateExtensionParams) GetState() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["state"].(string)
+	return value, ok
+}
+
+func (p *CreateExtensionParams) SetType(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["type"] = v
+}
+
+func (p *CreateExtensionParams) ResetType() {
+	if p.p != nil && p.p["type"] != nil {
+		delete(p.p, "type")
+	}
+}
+
+func (p *CreateExtensionParams) GetType() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["type"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new CreateExtensionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewCreateExtensionParams(name string, extensionType string) *CreateExtensionParams {
+	p := &CreateExtensionParams{}
+	p.p = make(map[string]interface{})
+	p.p["name"] = name
+	p.p["type"] = extensionType
+	return p
+}
+
+// Create an extension
+func (s *ExtensionService) CreateExtension(p *CreateExtensionParams) (*CreateExtensionResponse, error) {
+	resp, err := s.cs.newPostRequest("createExtension", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r CreateExtensionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type CreateExtensionResponse struct {
+	Created       string                             `json:"created"`
+	Description   string                             `json:"description"`
+	Details       map[string]string                  `json:"details"`
+	Id            string                             `json:"id"`
+	Isuserdefined bool                               `json:"isuserdefined"`
+	JobID         string                             `json:"jobid"`
+	Jobstatus     int                                `json:"jobstatus"`
+	Name          string                             `json:"name"`
+	Path          string                             `json:"path"`
+	Pathready     bool                               `json:"pathready"`
+	Removed       string                             `json:"removed"`
+	Resources     []CreateExtensionResponseResources `json:"resources"`
+	State         string                             `json:"state"`
+	Type          string                             `json:"type"`
+}
+
+type CreateExtensionResponseResources struct {
+	Created string            `json:"created"`
+	Details map[string]string `json:"details"`
+	Id      string            `json:"id"`
+	Name    string            `json:"name"`
+	Type    string            `json:"type"`
+}
+
+type DeleteCustomActionParams struct {
+	p map[string]interface{}
+}
+
+func (p *DeleteCustomActionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["id"]; found {
+		u.Set("id", v.(string))
+	}
+	return u
+}
+
+func (p *DeleteCustomActionParams) SetId(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["id"] = v
+}
+
+func (p *DeleteCustomActionParams) ResetId() {
+	if p.p != nil && p.p["id"] != nil {
+		delete(p.p, "id")
+	}
+}
+
+func (p *DeleteCustomActionParams) GetId() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["id"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new DeleteCustomActionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewDeleteCustomActionParams() *DeleteCustomActionParams {
+	p := &DeleteCustomActionParams{}
+	p.p = make(map[string]interface{})
+	return p
+}
+
+// Delete the custom action
+func (s *ExtensionService) DeleteCustomAction(p *DeleteCustomActionParams) (*DeleteCustomActionResponse, error) {
+	resp, err := s.cs.newPostRequest("deleteCustomAction", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r DeleteCustomActionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type DeleteCustomActionResponse struct {
+	Displaytext string `json:"displaytext"`
+	JobID       string `json:"jobid"`
+	Jobstatus   int    `json:"jobstatus"`
+	Success     bool   `json:"success"`
+}
+
+func (r *DeleteCustomActionResponse) UnmarshalJSON(b []byte) error {
+	var m map[string]interface{}
+	err := json.Unmarshal(b, &m)
+	if err != nil {
+		return err
+	}
+
+	if success, ok := m["success"].(string); ok {
+		m["success"] = success == "true"
+		b, err = json.Marshal(m)
+		if err != nil {
+			return err
+		}
+	}
+
+	if ostypeid, ok := m["ostypeid"].(float64); ok {
+		m["ostypeid"] = strconv.Itoa(int(ostypeid))
+		b, err = json.Marshal(m)
+		if err != nil {
+			return err
+		}
+	}
+
+	type alias DeleteCustomActionResponse
+	return json.Unmarshal(b, (*alias)(r))
+}
+
+type DeleteExtensionParams struct {
+	p map[string]interface{}
+}
+
+func (p *DeleteExtensionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["cleanup"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("cleanup", vv)
+	}
+	if v, found := p.p["id"]; found {
+		u.Set("id", v.(string))
+	}
+	return u
+}
+
+func (p *DeleteExtensionParams) SetCleanup(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["cleanup"] = v
+}
+
+func (p *DeleteExtensionParams) ResetCleanup() {
+	if p.p != nil && p.p["cleanup"] != nil {
+		delete(p.p, "cleanup")
+	}
+}
+
+func (p *DeleteExtensionParams) GetCleanup() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["cleanup"].(bool)
+	return value, ok
+}
+
+func (p *DeleteExtensionParams) SetId(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["id"] = v
+}
+
+func (p *DeleteExtensionParams) ResetId() {
+	if p.p != nil && p.p["id"] != nil {
+		delete(p.p, "id")
+	}
+}
+
+func (p *DeleteExtensionParams) GetId() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["id"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new DeleteExtensionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewDeleteExtensionParams() *DeleteExtensionParams {
+	p := &DeleteExtensionParams{}
+	p.p = make(map[string]interface{})
+	return p
+}
+
+// Delete the extensions
+func (s *ExtensionService) DeleteExtension(p *DeleteExtensionParams) (*DeleteExtensionResponse, error) {
+	resp, err := s.cs.newPostRequest("deleteExtension", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r DeleteExtensionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type DeleteExtensionResponse struct {
+	Created       string                             `json:"created"`
+	Description   string                             `json:"description"`
+	Details       map[string]string                  `json:"details"`
+	Id            string                             `json:"id"`
+	Isuserdefined bool                               `json:"isuserdefined"`
+	JobID         string                             `json:"jobid"`
+	Jobstatus     int                                `json:"jobstatus"`
+	Name          string                             `json:"name"`
+	Path          string                             `json:"path"`
+	Pathready     bool                               `json:"pathready"`
+	Removed       string                             `json:"removed"`
+	Resources     []DeleteExtensionResponseResources `json:"resources"`
+	State         string                             `json:"state"`
+	Type          string                             `json:"type"`
+}
+
+type DeleteExtensionResponseResources struct {
+	Created string            `json:"created"`
+	Details map[string]string `json:"details"`
+	Id      string            `json:"id"`
+	Name    string            `json:"name"`
+	Type    string            `json:"type"`
+}
+
+type ListCustomActionsParams struct {
+	p map[string]interface{}
+}
+
+func (p *ListCustomActionsParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["enabled"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("enabled", vv)
+	}
+	if v, found := p.p["extensionid"]; found {
+		u.Set("extensionid", v.(string))
+	}
+	if v, found := p.p["id"]; found {
+		u.Set("id", v.(string))
+	}
+	if v, found := p.p["keyword"]; found {
+		u.Set("keyword", v.(string))
+	}
+	if v, found := p.p["name"]; found {
+		u.Set("name", v.(string))
+	}
+	if v, found := p.p["page"]; found {
+		vv := strconv.Itoa(v.(int))
+		u.Set("page", vv)
+	}
+	if v, found := p.p["pagesize"]; found {
+		vv := strconv.Itoa(v.(int))
+		u.Set("pagesize", vv)
+	}
+	if v, found := p.p["resourceid"]; found {
+		u.Set("resourceid", v.(string))
+	}
+	if v, found := p.p["resourcetype"]; found {
+		u.Set("resourcetype", v.(string))
+	}
+	return u
+}
+
+func (p *ListCustomActionsParams) SetEnabled(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["enabled"] = v
+}
+
+func (p *ListCustomActionsParams) ResetEnabled() {
+	if p.p != nil && p.p["enabled"] != nil {
+		delete(p.p, "enabled")
+	}
+}
+
+func (p *ListCustomActionsParams) GetEnabled() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["enabled"].(bool)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetExtensionid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["extensionid"] = v
+}
+
+func (p *ListCustomActionsParams) ResetExtensionid() {
+	if p.p != nil && p.p["extensionid"] != nil {
+		delete(p.p, "extensionid")
+	}
+}
+
+func (p *ListCustomActionsParams) GetExtensionid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["extensionid"].(string)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetId(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["id"] = v
+}
+
+func (p *ListCustomActionsParams) ResetId() {
+	if p.p != nil && p.p["id"] != nil {
+		delete(p.p, "id")
+	}
+}
+
+func (p *ListCustomActionsParams) GetId() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["id"].(string)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetKeyword(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["keyword"] = v
+}
+
+func (p *ListCustomActionsParams) ResetKeyword() {
+	if p.p != nil && p.p["keyword"] != nil {
+		delete(p.p, "keyword")
+	}
+}
+
+func (p *ListCustomActionsParams) GetKeyword() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["keyword"].(string)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetName(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["name"] = v
+}
+
+func (p *ListCustomActionsParams) ResetName() {
+	if p.p != nil && p.p["name"] != nil {
+		delete(p.p, "name")
+	}
+}
+
+func (p *ListCustomActionsParams) GetName() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["name"].(string)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetPage(v int) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["page"] = v
+}
+
+func (p *ListCustomActionsParams) ResetPage() {
+	if p.p != nil && p.p["page"] != nil {
+		delete(p.p, "page")
+	}
+}
+
+func (p *ListCustomActionsParams) GetPage() (int, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["page"].(int)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetPagesize(v int) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["pagesize"] = v
+}
+
+func (p *ListCustomActionsParams) ResetPagesize() {
+	if p.p != nil && p.p["pagesize"] != nil {
+		delete(p.p, "pagesize")
+	}
+}
+
+func (p *ListCustomActionsParams) GetPagesize() (int, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["pagesize"].(int)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetResourceid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourceid"] = v
+}
+
+func (p *ListCustomActionsParams) ResetResourceid() {
+	if p.p != nil && p.p["resourceid"] != nil {
+		delete(p.p, "resourceid")
+	}
+}
+
+func (p *ListCustomActionsParams) GetResourceid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourceid"].(string)
+	return value, ok
+}
+
+func (p *ListCustomActionsParams) SetResourcetype(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourcetype"] = v
+}
+
+func (p *ListCustomActionsParams) ResetResourcetype() {
+	if p.p != nil && p.p["resourcetype"] != nil {
+		delete(p.p, "resourcetype")
+	}
+}
+
+func (p *ListCustomActionsParams) GetResourcetype() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourcetype"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new ListCustomActionsParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewListCustomActionsParams() *ListCustomActionsParams {
+	p := &ListCustomActionsParams{}
+	p.p = make(map[string]interface{})
+	return p
+}
+
+// This is a courtesy helper function, which in some cases may not work as expected!
+func (s *ExtensionService) GetCustomActionID(name string, opts ...OptionFunc) (string, int, error) {
+	p := &ListCustomActionsParams{}
+	p.p = make(map[string]interface{})
+
+	p.p["name"] = name
+
+	for _, fn := range append(s.cs.options, opts...) {
+		if err := fn(s.cs, p); err != nil {
+			return "", -1, err
+		}
+	}
+
+	l, err := s.ListCustomActions(p)
+	if err != nil {
+		return "", -1, err
+	}
+
+	if l.Count == 0 {
+		return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
+	}
+
+	if l.Count == 1 {
+		return l.CustomActions[0].Id, l.Count, nil
+	}
+
+	if l.Count > 1 {
+		for _, v := range l.CustomActions {
+			if v.Name == name {
+				return v.Id, l.Count, nil
+			}
+		}
+	}
+	return "", l.Count, fmt.Errorf("Could not find an exact match for %s: %+v", name, l)
+}
+
+// This is a courtesy helper function, which in some cases may not work as expected!
+func (s *ExtensionService) GetCustomActionByName(name string, opts ...OptionFunc) (*CustomAction, int, error) {
+	id, count, err := s.GetCustomActionID(name, opts...)
+	if err != nil {
+		return nil, count, err
+	}
+
+	r, count, err := s.GetCustomActionByID(id, opts...)
+	if err != nil {
+		return nil, count, err
+	}
+	return r, count, nil
+}
+
+// This is a courtesy helper function, which in some cases may not work as expected!
+func (s *ExtensionService) GetCustomActionByID(id string, opts ...OptionFunc) (*CustomAction, int, error) {
+	p := &ListCustomActionsParams{}
+	p.p = make(map[string]interface{})
+
+	p.p["id"] = id
+
+	for _, fn := range append(s.cs.options, opts...) {
+		if err := fn(s.cs, p); err != nil {
+			return nil, -1, err
+		}
+	}
+
+	l, err := s.ListCustomActions(p)
+	if err != nil {
+		if strings.Contains(err.Error(), fmt.Sprintf(
+			"Invalid parameter id value=%s due to incorrect long value format, "+
+				"or entity does not exist", id)) {
+			return nil, 0, fmt.Errorf("No match found for %s: %+v", id, l)
+		}
+		return nil, -1, err
+	}
+
+	if l.Count == 0 {
+		return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
+	}
+
+	if l.Count == 1 {
+		return l.CustomActions[0], l.Count, nil
+	}
+	return nil, l.Count, fmt.Errorf("There is more then one result for CustomAction UUID: %s!", id)
+}
+
+// Lists the custom actions
+func (s *ExtensionService) ListCustomActions(p *ListCustomActionsParams) (*ListCustomActionsResponse, error) {
+	resp, err := s.cs.newRequest("listCustomActions", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r ListCustomActionsResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type ListCustomActionsResponse struct {
+	Count         int             `json:"count"`
+	CustomActions []*CustomAction `json:"customaction"`
+}
+
+type CustomAction struct {
+	Allowedroletypes []string                 `json:"allowedroletypes"`
+	Created          string                   `json:"created"`
+	Description      string                   `json:"description"`
+	Details          map[string]string        `json:"details"`
+	Enabled          bool                     `json:"enabled"`
+	Errormessage     string                   `json:"errormessage"`
+	Extensionid      string                   `json:"extensionid"`
+	Extensionname    string                   `json:"extensionname"`
+	Id               string                   `json:"id"`
+	JobID            string                   `json:"jobid"`
+	Jobstatus        int                      `json:"jobstatus"`
+	Name             string                   `json:"name"`
+	Parameters       []CustomActionParameters `json:"parameters"`
+	Resourcetype     string                   `json:"resourcetype"`
+	Successmessage   string                   `json:"successmessage"`
+	Timeout          int                      `json:"timeout"`
+}
+
+type CustomActionParameters struct {
+	Name             string   `json:"name"`
+	Required         bool     `json:"required"`
+	Type             string   `json:"type"`
+	Validationformat string   `json:"validationformat"`
+	Valueoptions     []string `json:"valueoptions"`
+}
+
+type ListExtensionsParams struct {
+	p map[string]interface{}
+}
+
+func (p *ListExtensionsParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["details"]; found {
+		vv := strings.Join(v.([]string), ",")
+		u.Set("details", vv)
+	}
+	if v, found := p.p["id"]; found {
+		u.Set("id", v.(string))
+	}
+	if v, found := p.p["keyword"]; found {
+		u.Set("keyword", v.(string))
+	}
+	if v, found := p.p["name"]; found {
+		u.Set("name", v.(string))
+	}
+	if v, found := p.p["page"]; found {
+		vv := strconv.Itoa(v.(int))
+		u.Set("page", vv)
+	}
+	if v, found := p.p["pagesize"]; found {
+		vv := strconv.Itoa(v.(int))
+		u.Set("pagesize", vv)
+	}
+	return u
+}
+
+func (p *ListExtensionsParams) SetDetails(v []string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["details"] = v
+}
+
+func (p *ListExtensionsParams) ResetDetails() {
+	if p.p != nil && p.p["details"] != nil {
+		delete(p.p, "details")
+	}
+}
+
+func (p *ListExtensionsParams) GetDetails() ([]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["details"].([]string)
+	return value, ok
+}
+
+func (p *ListExtensionsParams) SetId(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["id"] = v
+}
+
+func (p *ListExtensionsParams) ResetId() {
+	if p.p != nil && p.p["id"] != nil {
+		delete(p.p, "id")
+	}
+}
+
+func (p *ListExtensionsParams) GetId() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["id"].(string)
+	return value, ok
+}
+
+func (p *ListExtensionsParams) SetKeyword(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["keyword"] = v
+}
+
+func (p *ListExtensionsParams) ResetKeyword() {
+	if p.p != nil && p.p["keyword"] != nil {
+		delete(p.p, "keyword")
+	}
+}
+
+func (p *ListExtensionsParams) GetKeyword() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["keyword"].(string)
+	return value, ok
+}
+
+func (p *ListExtensionsParams) SetName(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["name"] = v
+}
+
+func (p *ListExtensionsParams) ResetName() {
+	if p.p != nil && p.p["name"] != nil {
+		delete(p.p, "name")
+	}
+}
+
+func (p *ListExtensionsParams) GetName() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["name"].(string)
+	return value, ok
+}
+
+func (p *ListExtensionsParams) SetPage(v int) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["page"] = v
+}
+
+func (p *ListExtensionsParams) ResetPage() {
+	if p.p != nil && p.p["page"] != nil {
+		delete(p.p, "page")
+	}
+}
+
+func (p *ListExtensionsParams) GetPage() (int, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["page"].(int)
+	return value, ok
+}
+
+func (p *ListExtensionsParams) SetPagesize(v int) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["pagesize"] = v
+}
+
+func (p *ListExtensionsParams) ResetPagesize() {
+	if p.p != nil && p.p["pagesize"] != nil {
+		delete(p.p, "pagesize")
+	}
+}
+
+func (p *ListExtensionsParams) GetPagesize() (int, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["pagesize"].(int)
+	return value, ok
+}
+
+// You should always use this function to get a new ListExtensionsParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewListExtensionsParams() *ListExtensionsParams {
+	p := &ListExtensionsParams{}
+	p.p = make(map[string]interface{})
+	return p
+}
+
+// This is a courtesy helper function, which in some cases may not work as expected!
+func (s *ExtensionService) GetExtensionID(name string, opts ...OptionFunc) (string, int, error) {
+	p := &ListExtensionsParams{}
+	p.p = make(map[string]interface{})
+
+	p.p["name"] = name
+
+	for _, fn := range append(s.cs.options, opts...) {
+		if err := fn(s.cs, p); err != nil {
+			return "", -1, err
+		}
+	}
+
+	l, err := s.ListExtensions(p)
+	if err != nil {
+		return "", -1, err
+	}
+
+	if l.Count == 0 {
+		return "", l.Count, fmt.Errorf("No match found for %s: %+v", name, l)
+	}
+
+	if l.Count == 1 {
+		return l.Extensions[0].Id, l.Count, nil
+	}
+
+	if l.Count > 1 {
+		for _, v := range l.Extensions {
+			if v.Name == name {
+				return v.Id, l.Count, nil
+			}
+		}
+	}
+	return "", l.Count, fmt.Errorf("Could not find an exact match for %s: %+v", name, l)
+}
+
+// This is a courtesy helper function, which in some cases may not work as expected!
+func (s *ExtensionService) GetExtensionByName(name string, opts ...OptionFunc) (*Extension, int, error) {
+	id, count, err := s.GetExtensionID(name, opts...)
+	if err != nil {
+		return nil, count, err
+	}
+
+	r, count, err := s.GetExtensionByID(id, opts...)
+	if err != nil {
+		return nil, count, err
+	}
+	return r, count, nil
+}
+
+// This is a courtesy helper function, which in some cases may not work as expected!
+func (s *ExtensionService) GetExtensionByID(id string, opts ...OptionFunc) (*Extension, int, error) {
+	p := &ListExtensionsParams{}
+	p.p = make(map[string]interface{})
+
+	p.p["id"] = id
+
+	for _, fn := range append(s.cs.options, opts...) {
+		if err := fn(s.cs, p); err != nil {
+			return nil, -1, err
+		}
+	}
+
+	l, err := s.ListExtensions(p)
+	if err != nil {
+		if strings.Contains(err.Error(), fmt.Sprintf(
+			"Invalid parameter id value=%s due to incorrect long value format, "+
+				"or entity does not exist", id)) {
+			return nil, 0, fmt.Errorf("No match found for %s: %+v", id, l)
+		}
+		return nil, -1, err
+	}
+
+	if l.Count == 0 {
+		return nil, l.Count, fmt.Errorf("No match found for %s: %+v", id, l)
+	}
+
+	if l.Count == 1 {
+		return l.Extensions[0], l.Count, nil
+	}
+	return nil, l.Count, fmt.Errorf("There is more then one result for Extension UUID: %s!", id)
+}
+
+// Lists extensions
+func (s *ExtensionService) ListExtensions(p *ListExtensionsParams) (*ListExtensionsResponse, error) {
+	resp, err := s.cs.newRequest("listExtensions", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r ListExtensionsResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type ListExtensionsResponse struct {
+	Count      int          `json:"count"`
+	Extensions []*Extension `json:"extension"`
+}
+
+type Extension struct {
+	Created       string               `json:"created"`
+	Description   string               `json:"description"`
+	Details       map[string]string    `json:"details"`
+	Id            string               `json:"id"`
+	Isuserdefined bool                 `json:"isuserdefined"`
+	JobID         string               `json:"jobid"`
+	Jobstatus     int                  `json:"jobstatus"`
+	Name          string               `json:"name"`
+	Path          string               `json:"path"`
+	Pathready     bool                 `json:"pathready"`
+	Removed       string               `json:"removed"`
+	Resources     []ExtensionResources `json:"resources"`
+	State         string               `json:"state"`
+	Type          string               `json:"type"`
+}
+
+type ExtensionResources struct {
+	Created string            `json:"created"`
+	Details map[string]string `json:"details"`
+	Id      string            `json:"id"`
+	Name    string            `json:"name"`
+	Type    string            `json:"type"`
+}
+
+type RegisterExtensionParams struct {
+	p map[string]interface{}
+}
+
+func (p *RegisterExtensionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["details"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("details[%d].%s", i, k), m[k])
+		}
+	}
+	if v, found := p.p["extensionid"]; found {
+		u.Set("extensionid", v.(string))
+	}
+	if v, found := p.p["resourceid"]; found {
+		u.Set("resourceid", v.(string))
+	}
+	if v, found := p.p["resourcetype"]; found {
+		u.Set("resourcetype", v.(string))
+	}
+	return u
+}
+
+func (p *RegisterExtensionParams) SetDetails(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["details"] = v
+}
+
+func (p *RegisterExtensionParams) ResetDetails() {
+	if p.p != nil && p.p["details"] != nil {
+		delete(p.p, "details")
+	}
+}
+
+func (p *RegisterExtensionParams) GetDetails() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["details"].(map[string]string)
+	return value, ok
+}
+
+func (p *RegisterExtensionParams) SetExtensionid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["extensionid"] = v
+}
+
+func (p *RegisterExtensionParams) ResetExtensionid() {
+	if p.p != nil && p.p["extensionid"] != nil {
+		delete(p.p, "extensionid")
+	}
+}
+
+func (p *RegisterExtensionParams) GetExtensionid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["extensionid"].(string)
+	return value, ok
+}
+
+func (p *RegisterExtensionParams) SetResourceid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourceid"] = v
+}
+
+func (p *RegisterExtensionParams) ResetResourceid() {
+	if p.p != nil && p.p["resourceid"] != nil {
+		delete(p.p, "resourceid")
+	}
+}
+
+func (p *RegisterExtensionParams) GetResourceid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourceid"].(string)
+	return value, ok
+}
+
+func (p *RegisterExtensionParams) SetResourcetype(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourcetype"] = v
+}
+
+func (p *RegisterExtensionParams) ResetResourcetype() {
+	if p.p != nil && p.p["resourcetype"] != nil {
+		delete(p.p, "resourcetype")
+	}
+}
+
+func (p *RegisterExtensionParams) GetResourcetype() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourcetype"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new RegisterExtensionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewRegisterExtensionParams(extensionid string, resourceid string, resourcetype string) *RegisterExtensionParams {
+	p := &RegisterExtensionParams{}
+	p.p = make(map[string]interface{})
+	p.p["extensionid"] = extensionid
+	p.p["resourceid"] = resourceid
+	p.p["resourcetype"] = resourcetype
+	return p
+}
+
+// Register an extension with a resource
+func (s *ExtensionService) RegisterExtension(p *RegisterExtensionParams) (*RegisterExtensionResponse, error) {
+	resp, err := s.cs.newPostRequest("registerExtension", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r RegisterExtensionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type RegisterExtensionResponse struct {
+	Created       string                               `json:"created"`
+	Description   string                               `json:"description"`
+	Details       map[string]string                    `json:"details"`
+	Id            string                               `json:"id"`
+	Isuserdefined bool                                 `json:"isuserdefined"`
+	JobID         string                               `json:"jobid"`
+	Jobstatus     int                                  `json:"jobstatus"`
+	Name          string                               `json:"name"`
+	Path          string                               `json:"path"`
+	Pathready     bool                                 `json:"pathready"`
+	Removed       string                               `json:"removed"`
+	Resources     []RegisterExtensionResponseResources `json:"resources"`
+	State         string                               `json:"state"`
+	Type          string                               `json:"type"`
+}
+
+type RegisterExtensionResponseResources struct {
+	Created string            `json:"created"`
+	Details map[string]string `json:"details"`
+	Id      string            `json:"id"`
+	Name    string            `json:"name"`
+	Type    string            `json:"type"`
+}
+
+type RunCustomActionParams struct {
+	p map[string]interface{}
+}
+
+func (p *RunCustomActionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["customactionid"]; found {
+		u.Set("customactionid", v.(string))
+	}
+	if v, found := p.p["parameters"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("parameters[%d].key", i), k)
+			u.Set(fmt.Sprintf("parameters[%d].value", i), m[k])
+		}
+	}
+	if v, found := p.p["resourceid"]; found {
+		u.Set("resourceid", v.(string))
+	}
+	if v, found := p.p["resourcetype"]; found {
+		u.Set("resourcetype", v.(string))
+	}
+	return u
+}
+
+func (p *RunCustomActionParams) SetCustomactionid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["customactionid"] = v
+}
+
+func (p *RunCustomActionParams) ResetCustomactionid() {
+	if p.p != nil && p.p["customactionid"] != nil {
+		delete(p.p, "customactionid")
+	}
+}
+
+func (p *RunCustomActionParams) GetCustomactionid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["customactionid"].(string)
+	return value, ok
+}
+
+func (p *RunCustomActionParams) SetParameters(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["parameters"] = v
+}
+
+func (p *RunCustomActionParams) ResetParameters() {
+	if p.p != nil && p.p["parameters"] != nil {
+		delete(p.p, "parameters")
+	}
+}
+
+func (p *RunCustomActionParams) GetParameters() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["parameters"].(map[string]string)
+	return value, ok
+}
+
+func (p *RunCustomActionParams) SetResourceid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourceid"] = v
+}
+
+func (p *RunCustomActionParams) ResetResourceid() {
+	if p.p != nil && p.p["resourceid"] != nil {
+		delete(p.p, "resourceid")
+	}
+}
+
+func (p *RunCustomActionParams) GetResourceid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourceid"].(string)
+	return value, ok
+}
+
+func (p *RunCustomActionParams) SetResourcetype(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourcetype"] = v
+}
+
+func (p *RunCustomActionParams) ResetResourcetype() {
+	if p.p != nil && p.p["resourcetype"] != nil {
+		delete(p.p, "resourcetype")
+	}
+}
+
+func (p *RunCustomActionParams) GetResourcetype() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourcetype"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new RunCustomActionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewRunCustomActionParams(customactionid string, resourceid string) *RunCustomActionParams {
+	p := &RunCustomActionParams{}
+	p.p = make(map[string]interface{})
+	p.p["customactionid"] = customactionid
+	p.p["resourceid"] = resourceid
+	return p
+}
+
+// Run the custom action
+func (s *ExtensionService) RunCustomAction(p *RunCustomActionParams) (*RunCustomActionResponse, error) {
+	resp, err := s.cs.newPostRequest("runCustomAction", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r RunCustomActionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	// If we have a async client, we need to wait for the async result
+	if s.cs.async {
+		b, err := s.cs.GetAsyncJobResult(r.JobID, s.cs.timeout)
+		if err != nil {
+			if err == AsyncTimeoutErr {
+				return &r, err
+			}
+			return nil, err
+		}
+
+		b, err = getRawValue(b)
+		if err != nil {
+			return nil, err
+		}
+
+		if err := json.Unmarshal(b, &r); err != nil {
+			return nil, err
+		}
+	}
+
+	return &r, nil
+}
+
+type RunCustomActionResponse struct {
+	Id        string            `json:"id"`
+	JobID     string            `json:"jobid"`
+	Jobstatus int               `json:"jobstatus"`
+	Name      string            `json:"name"`
+	Result    map[string]string `json:"result"`
+	Success   bool              `json:"success"`
+}
+
+type UnregisterExtensionParams struct {
+	p map[string]interface{}
+}
+
+func (p *UnregisterExtensionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["extensionid"]; found {
+		u.Set("extensionid", v.(string))
+	}
+	if v, found := p.p["resourceid"]; found {
+		u.Set("resourceid", v.(string))
+	}
+	if v, found := p.p["resourcetype"]; found {
+		u.Set("resourcetype", v.(string))
+	}
+	return u
+}
+
+func (p *UnregisterExtensionParams) SetExtensionid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["extensionid"] = v
+}
+
+func (p *UnregisterExtensionParams) ResetExtensionid() {
+	if p.p != nil && p.p["extensionid"] != nil {
+		delete(p.p, "extensionid")
+	}
+}
+
+func (p *UnregisterExtensionParams) GetExtensionid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["extensionid"].(string)
+	return value, ok
+}
+
+func (p *UnregisterExtensionParams) SetResourceid(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourceid"] = v
+}
+
+func (p *UnregisterExtensionParams) ResetResourceid() {
+	if p.p != nil && p.p["resourceid"] != nil {
+		delete(p.p, "resourceid")
+	}
+}
+
+func (p *UnregisterExtensionParams) GetResourceid() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourceid"].(string)
+	return value, ok
+}
+
+func (p *UnregisterExtensionParams) SetResourcetype(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourcetype"] = v
+}
+
+func (p *UnregisterExtensionParams) ResetResourcetype() {
+	if p.p != nil && p.p["resourcetype"] != nil {
+		delete(p.p, "resourcetype")
+	}
+}
+
+func (p *UnregisterExtensionParams) GetResourcetype() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourcetype"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new UnregisterExtensionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewUnregisterExtensionParams(extensionid string, resourceid string, resourcetype string) *UnregisterExtensionParams {
+	p := &UnregisterExtensionParams{}
+	p.p = make(map[string]interface{})
+	p.p["extensionid"] = extensionid
+	p.p["resourceid"] = resourceid
+	p.p["resourcetype"] = resourcetype
+	return p
+}
+
+// Unregister an extension with a resource
+func (s *ExtensionService) UnregisterExtension(p *UnregisterExtensionParams) (*UnregisterExtensionResponse, error) {
+	resp, err := s.cs.newPostRequest("unregisterExtension", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r UnregisterExtensionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type UnregisterExtensionResponse struct {
+	Created       string                                 `json:"created"`
+	Description   string                                 `json:"description"`
+	Details       map[string]string                      `json:"details"`
+	Id            string                                 `json:"id"`
+	Isuserdefined bool                                   `json:"isuserdefined"`
+	JobID         string                                 `json:"jobid"`
+	Jobstatus     int                                    `json:"jobstatus"`
+	Name          string                                 `json:"name"`
+	Path          string                                 `json:"path"`
+	Pathready     bool                                   `json:"pathready"`
+	Removed       string                                 `json:"removed"`
+	Resources     []UnregisterExtensionResponseResources `json:"resources"`
+	State         string                                 `json:"state"`
+	Type          string                                 `json:"type"`
+}
+
+type UnregisterExtensionResponseResources struct {
+	Created string            `json:"created"`
+	Details map[string]string `json:"details"`
+	Id      string            `json:"id"`
+	Name    string            `json:"name"`
+	Type    string            `json:"type"`
+}
+
+type UpdateCustomActionParams struct {
+	p map[string]interface{}
+}
+
+func (p *UpdateCustomActionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["allowedroletypes"]; found {
+		vv := strings.Join(v.([]string), ",")
+		u.Set("allowedroletypes", vv)
+	}
+	if v, found := p.p["cleanupdetails"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("cleanupdetails", vv)
+	}
+	if v, found := p.p["cleanupparameters"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("cleanupparameters", vv)
+	}
+	if v, found := p.p["description"]; found {
+		u.Set("description", v.(string))
+	}
+	if v, found := p.p["details"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("details[%d].%s", i, k), m[k])
+		}
+	}
+	if v, found := p.p["enabled"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("enabled", vv)
+	}
+	if v, found := p.p["errormessage"]; found {
+		u.Set("errormessage", v.(string))
+	}
+	if v, found := p.p["id"]; found {
+		u.Set("id", v.(string))
+	}
+	if v, found := p.p["parameters"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("parameters[%d].key", i), k)
+			u.Set(fmt.Sprintf("parameters[%d].value", i), m[k])
+		}
+	}
+	if v, found := p.p["resourcetype"]; found {
+		u.Set("resourcetype", v.(string))
+	}
+	if v, found := p.p["successmessage"]; found {
+		u.Set("successmessage", v.(string))
+	}
+	if v, found := p.p["timeout"]; found {
+		vv := strconv.Itoa(v.(int))
+		u.Set("timeout", vv)
+	}
+	return u
+}
+
+func (p *UpdateCustomActionParams) SetAllowedroletypes(v []string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["allowedroletypes"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetAllowedroletypes() {
+	if p.p != nil && p.p["allowedroletypes"] != nil {
+		delete(p.p, "allowedroletypes")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetAllowedroletypes() ([]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["allowedroletypes"].([]string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetCleanupdetails(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["cleanupdetails"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetCleanupdetails() {
+	if p.p != nil && p.p["cleanupdetails"] != nil {
+		delete(p.p, "cleanupdetails")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetCleanupdetails() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["cleanupdetails"].(bool)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetCleanupparameters(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["cleanupparameters"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetCleanupparameters() {
+	if p.p != nil && p.p["cleanupparameters"] != nil {
+		delete(p.p, "cleanupparameters")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetCleanupparameters() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["cleanupparameters"].(bool)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetDescription(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["description"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetDescription() {
+	if p.p != nil && p.p["description"] != nil {
+		delete(p.p, "description")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetDescription() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["description"].(string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetDetails(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["details"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetDetails() {
+	if p.p != nil && p.p["details"] != nil {
+		delete(p.p, "details")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetDetails() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["details"].(map[string]string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetEnabled(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["enabled"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetEnabled() {
+	if p.p != nil && p.p["enabled"] != nil {
+		delete(p.p, "enabled")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetEnabled() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["enabled"].(bool)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetErrormessage(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["errormessage"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetErrormessage() {
+	if p.p != nil && p.p["errormessage"] != nil {
+		delete(p.p, "errormessage")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetErrormessage() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["errormessage"].(string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetId(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["id"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetId() {
+	if p.p != nil && p.p["id"] != nil {
+		delete(p.p, "id")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetId() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["id"].(string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetParameters(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["parameters"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetParameters() {
+	if p.p != nil && p.p["parameters"] != nil {
+		delete(p.p, "parameters")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetParameters() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["parameters"].(map[string]string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetResourcetype(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["resourcetype"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetResourcetype() {
+	if p.p != nil && p.p["resourcetype"] != nil {
+		delete(p.p, "resourcetype")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetResourcetype() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["resourcetype"].(string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetSuccessmessage(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["successmessage"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetSuccessmessage() {
+	if p.p != nil && p.p["successmessage"] != nil {
+		delete(p.p, "successmessage")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetSuccessmessage() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["successmessage"].(string)
+	return value, ok
+}
+
+func (p *UpdateCustomActionParams) SetTimeout(v int) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["timeout"] = v
+}
+
+func (p *UpdateCustomActionParams) ResetTimeout() {
+	if p.p != nil && p.p["timeout"] != nil {
+		delete(p.p, "timeout")
+	}
+}
+
+func (p *UpdateCustomActionParams) GetTimeout() (int, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["timeout"].(int)
+	return value, ok
+}
+
+// You should always use this function to get a new UpdateCustomActionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewUpdateCustomActionParams(id string) *UpdateCustomActionParams {
+	p := &UpdateCustomActionParams{}
+	p.p = make(map[string]interface{})
+	p.p["id"] = id
+	return p
+}
+
+// Update the custom action
+func (s *ExtensionService) UpdateCustomAction(p *UpdateCustomActionParams) (*UpdateCustomActionResponse, error) {
+	resp, err := s.cs.newPostRequest("updateCustomAction", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r UpdateCustomActionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type UpdateCustomActionResponse struct {
+	Displaytext string `json:"displaytext"`
+	JobID       string `json:"jobid"`
+	Jobstatus   int    `json:"jobstatus"`
+	Success     bool   `json:"success"`
+}
+
+func (r *UpdateCustomActionResponse) UnmarshalJSON(b []byte) error {
+	var m map[string]interface{}
+	err := json.Unmarshal(b, &m)
+	if err != nil {
+		return err
+	}
+
+	if success, ok := m["success"].(string); ok {
+		m["success"] = success == "true"
+		b, err = json.Marshal(m)
+		if err != nil {
+			return err
+		}
+	}
+
+	if ostypeid, ok := m["ostypeid"].(float64); ok {
+		m["ostypeid"] = strconv.Itoa(int(ostypeid))
+		b, err = json.Marshal(m)
+		if err != nil {
+			return err
+		}
+	}
+
+	type alias UpdateCustomActionResponse
+	return json.Unmarshal(b, (*alias)(r))
+}
+
+type UpdateExtensionParams struct {
+	p map[string]interface{}
+}
+
+func (p *UpdateExtensionParams) toURLValues() url.Values {
+	u := url.Values{}
+	if p.p == nil {
+		return u
+	}
+	if v, found := p.p["cleanupdetails"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("cleanupdetails", vv)
+	}
+	if v, found := p.p["description"]; found {
+		u.Set("description", v.(string))
+	}
+	if v, found := p.p["details"]; found {
+		m := v.(map[string]string)
+		for i, k := range getSortedKeysFromMap(m) {
+			u.Set(fmt.Sprintf("details[%d].%s", i, k), m[k])
+		}
+	}
+	if v, found := p.p["id"]; found {
+		u.Set("id", v.(string))
+	}
+	if v, found := p.p["orchestratorrequirespreparevm"]; found {
+		vv := strconv.FormatBool(v.(bool))
+		u.Set("orchestratorrequirespreparevm", vv)
+	}
+	if v, found := p.p["state"]; found {
+		u.Set("state", v.(string))
+	}
+	return u
+}
+
+func (p *UpdateExtensionParams) SetCleanupdetails(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["cleanupdetails"] = v
+}
+
+func (p *UpdateExtensionParams) ResetCleanupdetails() {
+	if p.p != nil && p.p["cleanupdetails"] != nil {
+		delete(p.p, "cleanupdetails")
+	}
+}
+
+func (p *UpdateExtensionParams) GetCleanupdetails() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["cleanupdetails"].(bool)
+	return value, ok
+}
+
+func (p *UpdateExtensionParams) SetDescription(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["description"] = v
+}
+
+func (p *UpdateExtensionParams) ResetDescription() {
+	if p.p != nil && p.p["description"] != nil {
+		delete(p.p, "description")
+	}
+}
+
+func (p *UpdateExtensionParams) GetDescription() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["description"].(string)
+	return value, ok
+}
+
+func (p *UpdateExtensionParams) SetDetails(v map[string]string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["details"] = v
+}
+
+func (p *UpdateExtensionParams) ResetDetails() {
+	if p.p != nil && p.p["details"] != nil {
+		delete(p.p, "details")
+	}
+}
+
+func (p *UpdateExtensionParams) GetDetails() (map[string]string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["details"].(map[string]string)
+	return value, ok
+}
+
+func (p *UpdateExtensionParams) SetId(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["id"] = v
+}
+
+func (p *UpdateExtensionParams) ResetId() {
+	if p.p != nil && p.p["id"] != nil {
+		delete(p.p, "id")
+	}
+}
+
+func (p *UpdateExtensionParams) GetId() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["id"].(string)
+	return value, ok
+}
+
+func (p *UpdateExtensionParams) SetOrchestratorrequirespreparevm(v bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["orchestratorrequirespreparevm"] = v
+}
+
+func (p *UpdateExtensionParams) ResetOrchestratorrequirespreparevm() {
+	if p.p != nil && p.p["orchestratorrequirespreparevm"] != nil {
+		delete(p.p, "orchestratorrequirespreparevm")
+	}
+}
+
+func (p *UpdateExtensionParams) GetOrchestratorrequirespreparevm() (bool, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["orchestratorrequirespreparevm"].(bool)
+	return value, ok
+}
+
+func (p *UpdateExtensionParams) SetState(v string) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	p.p["state"] = v
+}
+
+func (p *UpdateExtensionParams) ResetState() {
+	if p.p != nil && p.p["state"] != nil {
+		delete(p.p, "state")
+	}
+}
+
+func (p *UpdateExtensionParams) GetState() (string, bool) {
+	if p.p == nil {
+		p.p = make(map[string]interface{})
+	}
+	value, ok := p.p["state"].(string)
+	return value, ok
+}
+
+// You should always use this function to get a new UpdateExtensionParams instance,
+// as then you are sure you have configured all required params
+func (s *ExtensionService) NewUpdateExtensionParams(id string) *UpdateExtensionParams {
+	p := &UpdateExtensionParams{}
+	p.p = make(map[string]interface{})
+	p.p["id"] = id
+	return p
+}
+
+// Update the extension
+func (s *ExtensionService) UpdateExtension(p *UpdateExtensionParams) (*UpdateExtensionResponse, error) {
+	resp, err := s.cs.newPostRequest("updateExtension", p.toURLValues())
+	if err != nil {
+		return nil, err
+	}
+
+	var r UpdateExtensionResponse
+	if err := json.Unmarshal(resp, &r); err != nil {
+		return nil, err
+	}
+
+	return &r, nil
+}
+
+type UpdateExtensionResponse struct {
+	Created       string                             `json:"created"`
+	Description   string                             `json:"description"`
+	Details       map[string]string                  `json:"details"`
+	Id            string                             `json:"id"`
+	Isuserdefined bool                               `json:"isuserdefined"`
+	JobID         string                             `json:"jobid"`
+	Jobstatus     int                                `json:"jobstatus"`
+	Name          string                             `json:"name"`
+	Path          string                             `json:"path"`
+	Pathready     bool                               `json:"pathready"`
+	Removed       string                             `json:"removed"`
+	Resources     []UpdateExtensionResponseResources `json:"resources"`
+	State         string                             `json:"state"`
+	Type          string                             `json:"type"`
+}
+
+type UpdateExtensionResponseResources struct {
+	Created string            `json:"created"`
+	Details map[string]string `json:"details"`
+	Id      string            `json:"id"`
+	Name    string            `json:"name"`
+	Type    string            `json:"type"`
+}
diff --git a/cloudstack/ExtensionService_mock.go b/cloudstack/ExtensionService_mock.go
new file mode 100644
index 0000000..c9e70c9
--- /dev/null
+++ b/cloudstack/ExtensionService_mock.go
@@ -0,0 +1,504 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: ./cloudstack/ExtensionService.go
+//
+// Generated by this command:
+//
+//	mockgen -destination=./cloudstack/ExtensionService_mock.go -package=cloudstack -copyright_file=header.txt -source=./cloudstack/ExtensionService.go
+//
+
+// Package cloudstack is a generated GoMock package.
+package cloudstack
+
+import (
+	reflect "reflect"
+
+	gomock "go.uber.org/mock/gomock"
+)
+
+// MockExtensionServiceIface is a mock of ExtensionServiceIface interface.
+type MockExtensionServiceIface struct {
+	ctrl     *gomock.Controller
+	recorder *MockExtensionServiceIfaceMockRecorder
+	isgomock struct{}
+}
+
+// MockExtensionServiceIfaceMockRecorder is the mock recorder for MockExtensionServiceIface.
+type MockExtensionServiceIfaceMockRecorder struct {
+	mock *MockExtensionServiceIface
+}
+
+// NewMockExtensionServiceIface creates a new mock instance.
+func NewMockExtensionServiceIface(ctrl *gomock.Controller) *MockExtensionServiceIface {
+	mock := &MockExtensionServiceIface{ctrl: ctrl}
+	mock.recorder = &MockExtensionServiceIfaceMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockExtensionServiceIface) EXPECT() *MockExtensionServiceIfaceMockRecorder {
+	return m.recorder
+}
+
+// AddCustomAction mocks base method.
+func (m *MockExtensionServiceIface) AddCustomAction(p *AddCustomActionParams) (*AddCustomActionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "AddCustomAction", p)
+	ret0, _ := ret[0].(*AddCustomActionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// AddCustomAction indicates an expected call of AddCustomAction.
+func (mr *MockExtensionServiceIfaceMockRecorder) AddCustomAction(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddCustomAction", reflect.TypeOf((*MockExtensionServiceIface)(nil).AddCustomAction), p)
+}
+
+// CreateExtension mocks base method.
+func (m *MockExtensionServiceIface) CreateExtension(p *CreateExtensionParams) (*CreateExtensionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CreateExtension", p)
+	ret0, _ := ret[0].(*CreateExtensionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// CreateExtension indicates an expected call of CreateExtension.
+func (mr *MockExtensionServiceIfaceMockRecorder) CreateExtension(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateExtension", reflect.TypeOf((*MockExtensionServiceIface)(nil).CreateExtension), p)
+}
+
+// DeleteCustomAction mocks base method.
+func (m *MockExtensionServiceIface) DeleteCustomAction(p *DeleteCustomActionParams) (*DeleteCustomActionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DeleteCustomAction", p)
+	ret0, _ := ret[0].(*DeleteCustomActionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// DeleteCustomAction indicates an expected call of DeleteCustomAction.
+func (mr *MockExtensionServiceIfaceMockRecorder) DeleteCustomAction(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCustomAction", reflect.TypeOf((*MockExtensionServiceIface)(nil).DeleteCustomAction), p)
+}
+
+// DeleteExtension mocks base method.
+func (m *MockExtensionServiceIface) DeleteExtension(p *DeleteExtensionParams) (*DeleteExtensionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "DeleteExtension", p)
+	ret0, _ := ret[0].(*DeleteExtensionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// DeleteExtension indicates an expected call of DeleteExtension.
+func (mr *MockExtensionServiceIfaceMockRecorder) DeleteExtension(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExtension", reflect.TypeOf((*MockExtensionServiceIface)(nil).DeleteExtension), p)
+}
+
+// GetCustomActionByID mocks base method.
+func (m *MockExtensionServiceIface) GetCustomActionByID(id string, opts ...OptionFunc) (*CustomAction, int, error) {
+	m.ctrl.T.Helper()
+	varargs := []any{id}
+	for _, a := range opts {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "GetCustomActionByID", varargs...)
+	ret0, _ := ret[0].(*CustomAction)
+	ret1, _ := ret[1].(int)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// GetCustomActionByID indicates an expected call of GetCustomActionByID.
+func (mr *MockExtensionServiceIfaceMockRecorder) GetCustomActionByID(id any, opts ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{id}, opts...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomActionByID", reflect.TypeOf((*MockExtensionServiceIface)(nil).GetCustomActionByID), varargs...)
+}
+
+// GetCustomActionByName mocks base method.
+func (m *MockExtensionServiceIface) GetCustomActionByName(name string, opts ...OptionFunc) (*CustomAction, int, error) {
+	m.ctrl.T.Helper()
+	varargs := []any{name}
+	for _, a := range opts {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "GetCustomActionByName", varargs...)
+	ret0, _ := ret[0].(*CustomAction)
+	ret1, _ := ret[1].(int)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// GetCustomActionByName indicates an expected call of GetCustomActionByName.
+func (mr *MockExtensionServiceIfaceMockRecorder) GetCustomActionByName(name any, opts ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{name}, opts...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomActionByName", reflect.TypeOf((*MockExtensionServiceIface)(nil).GetCustomActionByName), varargs...)
+}
+
+// GetCustomActionID mocks base method.
+func (m *MockExtensionServiceIface) GetCustomActionID(name string, opts ...OptionFunc) (string, int, error) {
+	m.ctrl.T.Helper()
+	varargs := []any{name}
+	for _, a := range opts {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "GetCustomActionID", varargs...)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(int)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// GetCustomActionID indicates an expected call of GetCustomActionID.
+func (mr *MockExtensionServiceIfaceMockRecorder) GetCustomActionID(name any, opts ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{name}, opts...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomActionID", reflect.TypeOf((*MockExtensionServiceIface)(nil).GetCustomActionID), varargs...)
+}
+
+// GetExtensionByID mocks base method.
+func (m *MockExtensionServiceIface) GetExtensionByID(id string, opts ...OptionFunc) (*Extension, int, error) {
+	m.ctrl.T.Helper()
+	varargs := []any{id}
+	for _, a := range opts {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "GetExtensionByID", varargs...)
+	ret0, _ := ret[0].(*Extension)
+	ret1, _ := ret[1].(int)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// GetExtensionByID indicates an expected call of GetExtensionByID.
+func (mr *MockExtensionServiceIfaceMockRecorder) GetExtensionByID(id any, opts ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{id}, opts...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtensionByID", reflect.TypeOf((*MockExtensionServiceIface)(nil).GetExtensionByID), varargs...)
+}
+
+// GetExtensionByName mocks base method.
+func (m *MockExtensionServiceIface) GetExtensionByName(name string, opts ...OptionFunc) (*Extension, int, error) {
+	m.ctrl.T.Helper()
+	varargs := []any{name}
+	for _, a := range opts {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "GetExtensionByName", varargs...)
+	ret0, _ := ret[0].(*Extension)
+	ret1, _ := ret[1].(int)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// GetExtensionByName indicates an expected call of GetExtensionByName.
+func (mr *MockExtensionServiceIfaceMockRecorder) GetExtensionByName(name any, opts ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{name}, opts...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtensionByName", reflect.TypeOf((*MockExtensionServiceIface)(nil).GetExtensionByName), varargs...)
+}
+
+// GetExtensionID mocks base method.
+func (m *MockExtensionServiceIface) GetExtensionID(name string, opts ...OptionFunc) (string, int, error) {
+	m.ctrl.T.Helper()
+	varargs := []any{name}
+	for _, a := range opts {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "GetExtensionID", varargs...)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(int)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
+}
+
+// GetExtensionID indicates an expected call of GetExtensionID.
+func (mr *MockExtensionServiceIfaceMockRecorder) GetExtensionID(name any, opts ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{name}, opts...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtensionID", reflect.TypeOf((*MockExtensionServiceIface)(nil).GetExtensionID), varargs...)
+}
+
+// ListCustomActions mocks base method.
+func (m *MockExtensionServiceIface) ListCustomActions(p *ListCustomActionsParams) (*ListCustomActionsResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ListCustomActions", p)
+	ret0, _ := ret[0].(*ListCustomActionsResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ListCustomActions indicates an expected call of ListCustomActions.
+func (mr *MockExtensionServiceIfaceMockRecorder) ListCustomActions(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCustomActions", reflect.TypeOf((*MockExtensionServiceIface)(nil).ListCustomActions), p)
+}
+
+// ListExtensions mocks base method.
+func (m *MockExtensionServiceIface) ListExtensions(p *ListExtensionsParams) (*ListExtensionsResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ListExtensions", p)
+	ret0, _ := ret[0].(*ListExtensionsResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ListExtensions indicates an expected call of ListExtensions.
+func (mr *MockExtensionServiceIfaceMockRecorder) ListExtensions(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListExtensions", reflect.TypeOf((*MockExtensionServiceIface)(nil).ListExtensions), p)
+}
+
+// NewAddCustomActionParams mocks base method.
+func (m *MockExtensionServiceIface) NewAddCustomActionParams(extensionid, name string) *AddCustomActionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewAddCustomActionParams", extensionid, name)
+	ret0, _ := ret[0].(*AddCustomActionParams)
+	return ret0
+}
+
+// NewAddCustomActionParams indicates an expected call of NewAddCustomActionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewAddCustomActionParams(extensionid, name any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAddCustomActionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewAddCustomActionParams), extensionid, name)
+}
+
+// NewCreateExtensionParams mocks base method.
+func (m *MockExtensionServiceIface) NewCreateExtensionParams(name, extensionType string) *CreateExtensionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewCreateExtensionParams", name, extensionType)
+	ret0, _ := ret[0].(*CreateExtensionParams)
+	return ret0
+}
+
+// NewCreateExtensionParams indicates an expected call of NewCreateExtensionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewCreateExtensionParams(name, extensionType any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewCreateExtensionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewCreateExtensionParams), name, extensionType)
+}
+
+// NewDeleteCustomActionParams mocks base method.
+func (m *MockExtensionServiceIface) NewDeleteCustomActionParams() *DeleteCustomActionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewDeleteCustomActionParams")
+	ret0, _ := ret[0].(*DeleteCustomActionParams)
+	return ret0
+}
+
+// NewDeleteCustomActionParams indicates an expected call of NewDeleteCustomActionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewDeleteCustomActionParams() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDeleteCustomActionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewDeleteCustomActionParams))
+}
+
+// NewDeleteExtensionParams mocks base method.
+func (m *MockExtensionServiceIface) NewDeleteExtensionParams() *DeleteExtensionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewDeleteExtensionParams")
+	ret0, _ := ret[0].(*DeleteExtensionParams)
+	return ret0
+}
+
+// NewDeleteExtensionParams indicates an expected call of NewDeleteExtensionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewDeleteExtensionParams() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDeleteExtensionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewDeleteExtensionParams))
+}
+
+// NewListCustomActionsParams mocks base method.
+func (m *MockExtensionServiceIface) NewListCustomActionsParams() *ListCustomActionsParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewListCustomActionsParams")
+	ret0, _ := ret[0].(*ListCustomActionsParams)
+	return ret0
+}
+
+// NewListCustomActionsParams indicates an expected call of NewListCustomActionsParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewListCustomActionsParams() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewListCustomActionsParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewListCustomActionsParams))
+}
+
+// NewListExtensionsParams mocks base method.
+func (m *MockExtensionServiceIface) NewListExtensionsParams() *ListExtensionsParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewListExtensionsParams")
+	ret0, _ := ret[0].(*ListExtensionsParams)
+	return ret0
+}
+
+// NewListExtensionsParams indicates an expected call of NewListExtensionsParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewListExtensionsParams() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewListExtensionsParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewListExtensionsParams))
+}
+
+// NewRegisterExtensionParams mocks base method.
+func (m *MockExtensionServiceIface) NewRegisterExtensionParams(extensionid, resourceid, resourcetype string) *RegisterExtensionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewRegisterExtensionParams", extensionid, resourceid, resourcetype)
+	ret0, _ := ret[0].(*RegisterExtensionParams)
+	return ret0
+}
+
+// NewRegisterExtensionParams indicates an expected call of NewRegisterExtensionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewRegisterExtensionParams(extensionid, resourceid, resourcetype any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewRegisterExtensionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewRegisterExtensionParams), extensionid, resourceid, resourcetype)
+}
+
+// NewRunCustomActionParams mocks base method.
+func (m *MockExtensionServiceIface) NewRunCustomActionParams(customactionid, resourceid string) *RunCustomActionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewRunCustomActionParams", customactionid, resourceid)
+	ret0, _ := ret[0].(*RunCustomActionParams)
+	return ret0
+}
+
+// NewRunCustomActionParams indicates an expected call of NewRunCustomActionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewRunCustomActionParams(customactionid, resourceid any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewRunCustomActionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewRunCustomActionParams), customactionid, resourceid)
+}
+
+// NewUnregisterExtensionParams mocks base method.
+func (m *MockExtensionServiceIface) NewUnregisterExtensionParams(extensionid, resourceid, resourcetype string) *UnregisterExtensionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewUnregisterExtensionParams", extensionid, resourceid, resourcetype)
+	ret0, _ := ret[0].(*UnregisterExtensionParams)
+	return ret0
+}
+
+// NewUnregisterExtensionParams indicates an expected call of NewUnregisterExtensionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewUnregisterExtensionParams(extensionid, resourceid, resourcetype any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewUnregisterExtensionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewUnregisterExtensionParams), extensionid, resourceid, resourcetype)
+}
+
+// NewUpdateCustomActionParams mocks base method.
+func (m *MockExtensionServiceIface) NewUpdateCustomActionParams(id string) *UpdateCustomActionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewUpdateCustomActionParams", id)
+	ret0, _ := ret[0].(*UpdateCustomActionParams)
+	return ret0
+}
+
+// NewUpdateCustomActionParams indicates an expected call of NewUpdateCustomActionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewUpdateCustomActionParams(id any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewUpdateCustomActionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewUpdateCustomActionParams), id)
+}
+
+// NewUpdateExtensionParams mocks base method.
+func (m *MockExtensionServiceIface) NewUpdateExtensionParams(id string) *UpdateExtensionParams {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "NewUpdateExtensionParams", id)
+	ret0, _ := ret[0].(*UpdateExtensionParams)
+	return ret0
+}
+
+// NewUpdateExtensionParams indicates an expected call of NewUpdateExtensionParams.
+func (mr *MockExtensionServiceIfaceMockRecorder) NewUpdateExtensionParams(id any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewUpdateExtensionParams", reflect.TypeOf((*MockExtensionServiceIface)(nil).NewUpdateExtensionParams), id)
+}
+
+// RegisterExtension mocks base method.
+func (m *MockExtensionServiceIface) RegisterExtension(p *RegisterExtensionParams) (*RegisterExtensionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "RegisterExtension", p)
+	ret0, _ := ret[0].(*RegisterExtensionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// RegisterExtension indicates an expected call of RegisterExtension.
+func (mr *MockExtensionServiceIfaceMockRecorder) RegisterExtension(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterExtension", reflect.TypeOf((*MockExtensionServiceIface)(nil).RegisterExtension), p)
+}
+
+// RunCustomAction mocks base method.
+func (m *MockExtensionServiceIface) RunCustomAction(p *RunCustomActionParams) (*RunCustomActionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "RunCustomAction", p)
+	ret0, _ := ret[0].(*RunCustomActionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// RunCustomAction indicates an expected call of RunCustomAction.
+func (mr *MockExtensionServiceIfaceMockRecorder) RunCustomAction(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunCustomAction", reflect.TypeOf((*MockExtensionServiceIface)(nil).RunCustomAction), p)
+}
+
+// UnregisterExtension mocks base method.
+func (m *MockExtensionServiceIface) UnregisterExtension(p *UnregisterExtensionParams) (*UnregisterExtensionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "UnregisterExtension", p)
+	ret0, _ := ret[0].(*UnregisterExtensionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// UnregisterExtension indicates an expected call of UnregisterExtension.
+func (mr *MockExtensionServiceIfaceMockRecorder) UnregisterExtension(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterExtension", reflect.TypeOf((*MockExtensionServiceIface)(nil).UnregisterExtension), p)
+}
+
+// UpdateCustomAction mocks base method.
+func (m *MockExtensionServiceIface) UpdateCustomAction(p *UpdateCustomActionParams) (*UpdateCustomActionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "UpdateCustomAction", p)
+	ret0, _ := ret[0].(*UpdateCustomActionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// UpdateCustomAction indicates an expected call of UpdateCustomAction.
+func (mr *MockExtensionServiceIfaceMockRecorder) UpdateCustomAction(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCustomAction", reflect.TypeOf((*MockExtensionServiceIface)(nil).UpdateCustomAction), p)
+}
+
+// UpdateExtension mocks base method.
+func (m *MockExtensionServiceIface) UpdateExtension(p *UpdateExtensionParams) (*UpdateExtensionResponse, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "UpdateExtension", p)
+	ret0, _ := ret[0].(*UpdateExtensionResponse)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// UpdateExtension indicates an expected call of UpdateExtension.
+func (mr *MockExtensionServiceIfaceMockRecorder) UpdateExtension(p any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExtension", reflect.TypeOf((*MockExtensionServiceIface)(nil).UpdateExtension), p)
+}
diff --git a/cloudstack/cloudstack.go b/cloudstack/cloudstack.go
index ee8df3c..ac54056 100644
--- a/cloudstack/cloudstack.go
+++ b/cloudstack/cloudstack.go
@@ -125,6 +125,7 @@ type CloudStackClient struct {
 	DiskOffering            DiskOfferingServiceIface
 	Domain                  DomainServiceIface
 	Event                   EventServiceIface
+	Extension               ExtensionServiceIface
 	Firewall                FirewallServiceIface
 	GuestOS                 GuestOSServiceIface
 	Host                    HostServiceIface
@@ -252,6 +253,7 @@ func newClient(apiurl string, apikey string, secret string, async bool, verifyss
 	cs.DiskOffering = NewDiskOfferingService(cs)
 	cs.Domain = NewDomainService(cs)
 	cs.Event = NewEventService(cs)
+	cs.Extension = NewExtensionService(cs)
 	cs.Firewall = NewFirewallService(cs)
 	cs.GuestOS = NewGuestOSService(cs)
 	cs.Host = NewHostService(cs)
@@ -352,6 +354,7 @@ func newMockClient(ctrl *gomock.Controller) *CloudStackClient {
 	cs.DiskOffering = NewMockDiskOfferingServiceIface(ctrl)
 	cs.Domain = NewMockDomainServiceIface(ctrl)
 	cs.Event = NewMockEventServiceIface(ctrl)
+	cs.Extension = NewMockExtensionServiceIface(ctrl)
 	cs.Firewall = NewMockFirewallServiceIface(ctrl)
 	cs.GuestOS = NewMockGuestOSServiceIface(ctrl)
 	cs.Host = NewMockHostServiceIface(ctrl)
@@ -1012,6 +1015,14 @@ func NewEventService(cs *CloudStackClient) EventServiceIface {
 	return &EventService{cs: cs}
 }
 
+type ExtensionService struct {
+	cs *CloudStackClient
+}
+
+func NewExtensionService(cs *CloudStackClient) ExtensionServiceIface {
+	return &ExtensionService{cs: cs}
+}
+
 type FirewallService struct {
 	cs *CloudStackClient
 }
diff --git a/generate/layout.go b/generate/layout.go
index a878a67..509a890 100644
--- a/generate/layout.go
+++ b/generate/layout.go
@@ -29,6 +29,19 @@ var layout = apiInfo{
 		"releaseBgpPeer",
 		"updateBgpPeer",
 	},
+	"ExtensionService": {
+		"addCustomAction",
+		"createExtension",
+		"deleteCustomAction",
+		"deleteExtension",
+		"listCustomActions",
+		"listExtensions",
+		"registerExtension",
+		"runCustomAction",
+		"unregisterExtension",
+		"updateCustomAction",
+		"updateExtension",
+	},
 	"LoadBalancerService": {
 		"assignCertToLoadBalancer",
 		"assignToGlobalLoadBalancerRule",
diff --git a/test/ExtensionService_test.go b/test/ExtensionService_test.go
new file mode 100644
index 0000000..7eff181
--- /dev/null
+++ b/test/ExtensionService_test.go
@@ -0,0 +1,191 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package test
+
+import (
+	"testing"
+
+	"github.com/apache/cloudstack-go/v2/cloudstack"
+)
+
+func TestExtensionService(t *testing.T) {
+	service := "ExtensionService"
+	response, err := readData(service)
+	if err != nil {
+		t.Skipf("Skipping test as %v", err)
+	}
+	server := CreateTestServer(t, response)
+	client := cloudstack.NewClient(server.URL, "APIKEY", "SECRETKEY", true)
+	defer server.Close()
+
+	testaddCustomAction := func(t *testing.T) {
+		if _, ok := response["addCustomAction"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewAddCustomActionParams("extensionid", "name")
+		r, err := client.Extension.AddCustomAction(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+		if r.Id == "" {
+			t.Errorf("Failed to parse response. ID not found")
+		}
+	}
+	t.Run("AddCustomAction", testaddCustomAction)
+
+	testcreateExtension := func(t *testing.T) {
+		if _, ok := response["createExtension"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewCreateExtensionParams("name", "type")
+		r, err := client.Extension.CreateExtension(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+		if r.Id == "" {
+			t.Errorf("Failed to parse response. ID not found")
+		}
+	}
+	t.Run("CreateExtension", testcreateExtension)
+
+	testdeleteCustomAction := func(t *testing.T) {
+		if _, ok := response["deleteCustomAction"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewDeleteCustomActionParams()
+		_, err := client.Extension.DeleteCustomAction(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+	}
+	t.Run("DeleteCustomAction", testdeleteCustomAction)
+
+	testdeleteExtension := func(t *testing.T) {
+		if _, ok := response["deleteExtension"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewDeleteExtensionParams()
+		r, err := client.Extension.DeleteExtension(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+		if r.Id == "" {
+			t.Errorf("Failed to parse response. ID not found")
+		}
+	}
+	t.Run("DeleteExtension", testdeleteExtension)
+
+	testlistCustomActions := func(t *testing.T) {
+		if _, ok := response["listCustomActions"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewListCustomActionsParams()
+		_, err := client.Extension.ListCustomActions(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+	}
+	t.Run("ListCustomActions", testlistCustomActions)
+
+	testlistExtensions := func(t *testing.T) {
+		if _, ok := response["listExtensions"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewListExtensionsParams()
+		_, err := client.Extension.ListExtensions(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+	}
+	t.Run("ListExtensions", testlistExtensions)
+
+	testregisterExtension := func(t *testing.T) {
+		if _, ok := response["registerExtension"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewRegisterExtensionParams("extensionid", "resourceid", "resourcetype")
+		r, err := client.Extension.RegisterExtension(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+		if r.Id == "" {
+			t.Errorf("Failed to parse response. ID not found")
+		}
+	}
+	t.Run("RegisterExtension", testregisterExtension)
+
+	testrunCustomAction := func(t *testing.T) {
+		if _, ok := response["runCustomAction"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewRunCustomActionParams("customactionid", "resourceid")
+		r, err := client.Extension.RunCustomAction(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+		if r.Id == "" {
+			t.Errorf("Failed to parse response. ID not found")
+		}
+	}
+	t.Run("RunCustomAction", testrunCustomAction)
+
+	testunregisterExtension := func(t *testing.T) {
+		if _, ok := response["unregisterExtension"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewUnregisterExtensionParams("extensionid", "resourceid", "resourcetype")
+		r, err := client.Extension.UnregisterExtension(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+		if r.Id == "" {
+			t.Errorf("Failed to parse response. ID not found")
+		}
+	}
+	t.Run("UnregisterExtension", testunregisterExtension)
+
+	testupdateCustomAction := func(t *testing.T) {
+		if _, ok := response["updateCustomAction"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewUpdateCustomActionParams("id")
+		_, err := client.Extension.UpdateCustomAction(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+	}
+	t.Run("UpdateCustomAction", testupdateCustomAction)
+
+	testupdateExtension := func(t *testing.T) {
+		if _, ok := response["updateExtension"]; !ok {
+			t.Skipf("Skipping as no json response is provided in testdata")
+		}
+		p := client.Extension.NewUpdateExtensionParams("id")
+		r, err := client.Extension.UpdateExtension(p)
+		if err != nil {
+			t.Errorf(err.Error())
+		}
+		if r.Id == "" {
+			t.Errorf("Failed to parse response. ID not found")
+		}
+	}
+	t.Run("UpdateExtension", testupdateExtension)
+
+}