github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/networking/v2/ports/requests.go (about)

     1  package ports
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"slices"
     8  
     9  	"github.com/vnpaycloud-console/gophercloud/v2"
    10  	"github.com/vnpaycloud-console/gophercloud/v2/pagination"
    11  )
    12  
    13  // ListOptsBuilder allows extensions to add additional parameters to the
    14  // List request.
    15  type ListOptsBuilder interface {
    16  	ToPortListQuery() (string, error)
    17  }
    18  
    19  // ListOpts allows the filtering and sorting of paginated collections through
    20  // the API. Filtering is achieved by passing in struct field values that map to
    21  // the port attributes you want to see returned. SortKey allows you to sort
    22  // by a particular port attribute. SortDir sets the direction, and is either
    23  // `asc' or `desc'. Marker and Limit are used for pagination.
    24  type ListOpts struct {
    25  	Status         string   `q:"status"`
    26  	Name           string   `q:"name"`
    27  	Description    string   `q:"description"`
    28  	AdminStateUp   *bool    `q:"admin_state_up"`
    29  	NetworkID      string   `q:"network_id"`
    30  	TenantID       string   `q:"tenant_id"`
    31  	ProjectID      string   `q:"project_id"`
    32  	DeviceOwner    string   `q:"device_owner"`
    33  	MACAddress     string   `q:"mac_address"`
    34  	ID             string   `q:"id"`
    35  	DeviceID       string   `q:"device_id"`
    36  	Limit          int      `q:"limit"`
    37  	Marker         string   `q:"marker"`
    38  	SortKey        string   `q:"sort_key"`
    39  	SortDir        string   `q:"sort_dir"`
    40  	Tags           string   `q:"tags"`
    41  	TagsAny        string   `q:"tags-any"`
    42  	NotTags        string   `q:"not-tags"`
    43  	NotTagsAny     string   `q:"not-tags-any"`
    44  	SecurityGroups []string `q:"security_groups"`
    45  	FixedIPs       []FixedIPOpts
    46  }
    47  
    48  type FixedIPOpts struct {
    49  	IPAddress       string
    50  	IPAddressSubstr string
    51  	SubnetID        string
    52  }
    53  
    54  func (f FixedIPOpts) toParams() []string {
    55  	var res []string
    56  	if f.IPAddress != "" {
    57  		res = append(res, fmt.Sprintf("ip_address=%s", f.IPAddress))
    58  	}
    59  	if f.IPAddressSubstr != "" {
    60  		res = append(res, fmt.Sprintf("ip_address_substr=%s", f.IPAddressSubstr))
    61  	}
    62  	if f.SubnetID != "" {
    63  		res = append(res, fmt.Sprintf("subnet_id=%s", f.SubnetID))
    64  	}
    65  	return res
    66  }
    67  
    68  // ToPortListQuery formats a ListOpts into a query string.
    69  func (opts ListOpts) ToPortListQuery() (string, error) {
    70  	q, err := gophercloud.BuildQueryString(opts)
    71  	params := q.Query()
    72  	for _, fixedIP := range opts.FixedIPs {
    73  		for _, fixedIPParam := range fixedIP.toParams() {
    74  			params.Add("fixed_ips", fixedIPParam)
    75  		}
    76  	}
    77  	q = &url.URL{RawQuery: params.Encode()}
    78  	return q.String(), err
    79  }
    80  
    81  // List returns a Pager which allows you to iterate over a collection of
    82  // ports. It accepts a ListOpts struct, which allows you to filter and sort
    83  // the returned collection for greater efficiency.
    84  //
    85  // Default policy settings return only those ports that are owned by the tenant
    86  // who submits the request, unless the request is submitted by a user with
    87  // administrative rights.
    88  func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
    89  	url := listURL(c)
    90  	if opts != nil {
    91  		query, err := opts.ToPortListQuery()
    92  		if err != nil {
    93  			return pagination.Pager{Err: err}
    94  		}
    95  		url += query
    96  	}
    97  	return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
    98  		return PortPage{pagination.LinkedPageBase{PageResult: r}}
    99  	})
   100  }
   101  
   102  // Get retrieves a specific port based on its unique ID.
   103  func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) {
   104  	resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil)
   105  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   106  	return
   107  }
   108  
   109  // CreateOptsBuilder allows extensions to add additional parameters to the
   110  // Create request.
   111  type CreateOptsBuilder interface {
   112  	ToPortCreateMap() (map[string]any, error)
   113  }
   114  
   115  // CreateOpts represents the attributes used when creating a new port.
   116  type CreateOpts struct {
   117  	NetworkID             string             `json:"network_id" required:"true"`
   118  	Name                  string             `json:"name,omitempty"`
   119  	Description           string             `json:"description,omitempty"`
   120  	AdminStateUp          *bool              `json:"admin_state_up,omitempty"`
   121  	MACAddress            string             `json:"mac_address,omitempty"`
   122  	FixedIPs              any                `json:"fixed_ips,omitempty"`
   123  	DeviceID              string             `json:"device_id,omitempty"`
   124  	DeviceOwner           string             `json:"device_owner,omitempty"`
   125  	TenantID              string             `json:"tenant_id,omitempty"`
   126  	ProjectID             string             `json:"project_id,omitempty"`
   127  	SecurityGroups        *[]string          `json:"security_groups,omitempty"`
   128  	AllowedAddressPairs   []AddressPair      `json:"allowed_address_pairs,omitempty"`
   129  	PropagateUplinkStatus *bool              `json:"propagate_uplink_status,omitempty"`
   130  	ValueSpecs            *map[string]string `json:"value_specs,omitempty"`
   131  	VirtualIp             *bool              `json:"virtual_ip,omitempty"`
   132  }
   133  
   134  // ToPortCreateMap builds a request body from CreateOpts.
   135  func (opts CreateOpts) ToPortCreateMap() (map[string]any, error) {
   136  	body, err := gophercloud.BuildRequestBody(opts, "port")
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	return AddValueSpecs(body)
   142  }
   143  
   144  // AddValueSpecs expands the 'value_specs' object and removes 'value_specs'
   145  // from the request body. It will return error if the value specs would overwrite
   146  // an existing field or contains forbidden keys.
   147  func AddValueSpecs(body map[string]any) (map[string]any, error) {
   148  	// Banned the same as in heat. See https://github.com/openstack/heat/blob/dd7319e373b88812cb18897f742b5196a07227ea/heat/engine/resources/openstack/neutron/neutron.py#L59
   149  	bannedKeys := []string{"shared", "tenant_id"}
   150  	port := body["port"].(map[string]any)
   151  
   152  	if port["value_specs"] != nil {
   153  		for k, v := range port["value_specs"].(map[string]any) {
   154  			if slices.Contains(bannedKeys, k) {
   155  				return nil, fmt.Errorf("forbidden key in value_specs: %s", k)
   156  			}
   157  			if _, ok := port[k]; ok {
   158  				return nil, fmt.Errorf("value_specs would overwrite key: %s", k)
   159  			}
   160  			port[k] = v
   161  		}
   162  		delete(port, "value_specs")
   163  	}
   164  	body["port"] = port
   165  
   166  	return body, nil
   167  }
   168  
   169  // Create accepts a CreateOpts struct and creates a new network using the values
   170  // provided. You must remember to provide a NetworkID value.
   171  func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
   172  	b, err := opts.ToPortCreateMap()
   173  	if err != nil {
   174  		r.Err = err
   175  		return
   176  	}
   177  	resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil)
   178  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   179  	return
   180  }
   181  
   182  // UpdateOptsBuilder allows extensions to add additional parameters to the
   183  // Update request.
   184  type UpdateOptsBuilder interface {
   185  	ToPortUpdateMap() (map[string]any, error)
   186  }
   187  
   188  // UpdateOpts represents the attributes used when updating an existing port.
   189  type UpdateOpts struct {
   190  	Name                  *string            `json:"name,omitempty"`
   191  	Description           *string            `json:"description,omitempty"`
   192  	AdminStateUp          *bool              `json:"admin_state_up,omitempty"`
   193  	FixedIPs              any                `json:"fixed_ips,omitempty"`
   194  	DeviceID              *string            `json:"device_id,omitempty"`
   195  	DeviceOwner           *string            `json:"device_owner,omitempty"`
   196  	SecurityGroups        *[]string          `json:"security_groups,omitempty"`
   197  	AllowedAddressPairs   *[]AddressPair     `json:"allowed_address_pairs,omitempty"`
   198  	PropagateUplinkStatus *bool              `json:"propagate_uplink_status,omitempty"`
   199  	ValueSpecs            *map[string]string `json:"value_specs,omitempty"`
   200  
   201  	// RevisionNumber implements extension:standard-attr-revisions. If != "" it
   202  	// will set revision_number=%s. If the revision number does not match, the
   203  	// update will fail.
   204  	RevisionNumber *int  `json:"-" h:"If-Match"`
   205  	VirtualIp      *bool `json:"virtual_ip,omitempty"`
   206  }
   207  
   208  // ToPortUpdateMap builds a request body from UpdateOpts.
   209  func (opts UpdateOpts) ToPortUpdateMap() (map[string]any, error) {
   210  	body, err := gophercloud.BuildRequestBody(opts, "port")
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	return AddValueSpecs(body)
   215  }
   216  
   217  // Update accepts a UpdateOpts struct and updates an existing port using the
   218  // values provided.
   219  func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
   220  	b, err := opts.ToPortUpdateMap()
   221  	if err != nil {
   222  		r.Err = err
   223  		return
   224  	}
   225  	h, err := gophercloud.BuildHeaders(opts)
   226  	if err != nil {
   227  		r.Err = err
   228  		return
   229  	}
   230  	for k := range h {
   231  		if k == "If-Match" {
   232  			h[k] = fmt.Sprintf("revision_number=%s", h[k])
   233  		}
   234  	}
   235  	resp, err := c.Put(ctx, updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
   236  		MoreHeaders: h,
   237  		OkCodes:     []int{200, 201},
   238  	})
   239  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   240  	return
   241  }
   242  
   243  // Delete accepts a unique ID and deletes the port associated with it.
   244  func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) {
   245  	resp, err := c.Delete(ctx, deleteURL(c, id), nil)
   246  	_, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
   247  	return
   248  }