SG rules: implement bulk create#3267
Conversation
| // CreateBulk is an operation which adds new security group rules and associates them | ||
| // with an existing security group (whose ID is specified in CreateOpts). | ||
| func CreateBulk(ctx context.Context, c *gophercloud.ServiceClient, opts []CreateOpts) (r CreateResult) { | ||
| createOpts := make([]map[string]any, len(opts)) |
There was a problem hiding this comment.
Alternatively, we can modify gophercloud.BuildRequestBody() to accept an array/slice. Perhaps this would be more user friendly? Do we have a preference?
The nice thing if we decide to make gophercloud.BuildRequestBody() accept slices or arrays, is that it does not require changing the signature function, allowing us to backport the change.
There was a problem hiding this comment.
I've moved the logic in BuildRequestBody(), as it makes more sense to me. PTAL
2671e82 to
324a110
Compare
pierreprinetti
left a comment
There was a problem hiding this comment.
What happens when you pass two rules.CreateOpts that refer each to a different, existing security group ID?
I haven't tried but I understand they can be different, from the doc. |
|
nope. |
|
Perhaps, but that's none of gophercloud concerns, is it? |
|
I also woulnd't preemptively block invalid calls; however it may be useful to note the limitation in a comment |
When passing an array or a slice to `BuildRequestBody()`, the `parent` string becomes mandatory. This makes it possible to build requests for bulk operations.
324a110 to
b1f8bbb
Compare
|
TL;DR: The In other words, I would suggest that I did a couple tests with cURL. The call looked like this: curl -sSi \
-X POST \
-H "X-Auth-Token: $(openstack token issue -f value -c id)" \
-H "content-type: application/json" \
--data-binary @sg.json \
"$(openstack catalog show network --format json | jq -r '.endpoints[] | select(.interface == "public") | .url')/v2.0/security-group-rules"When the endpoint is called with a valid payload, it returns a JSON slice containing all the created rules. For example, when {
"security_group_rules": [
{
"direction": "ingress",
"port_range_min": "443",
"ethertype": "IPv4",
"port_range_max": "443",
"protocol": "tcp",
"security_group_id": "f9202b90-a34e-4804-9b72-60a9cda7bcc1"
},
{
"direction": "ingress",
"port_range_min": "80",
"ethertype": "IPv4",
"port_range_max": "80",
"protocol": "tcp",
"security_group_id": "f9202b90-a34e-4804-9b72-60a9cda7bcc1"
}
]
}...then the response looks like this: However when the payload contains one invalid security group rule, in whichever position: {
"security_group_rules": [
{
"direction": "ingress",
"port_range_min": "443",
"ethertype": "IPv4",
"port_range_max": "443",
"protocol": "tcp",
"security_group_id": "f9202b90-a34e-4804-9b72-60a9cda7bcc1"
},
{
"direction": "ingress",
"port_range_min": "81",
"ethertype": "IPv4",
"port_range_max": "79",
"protocol": "tcp",
"security_group_id": "f9202b90-a34e-4804-9b72-60a9cda7bcc1"
}
]
}...then none of them is created, and the response looks like this: I have also tested a payload with two errored rules:
and only one error is returned (in my case that was the first encountered, which is the different secgroup ID). Lastly, an empty slice of rules: {"security_group_rules":[]}Result: Based on these experiments, I think it's fair to say that as long as Neutron is healthy there are only two possible results to a call to the bulk-create endpoint:
|
|
oh you have added |
|
Can you please change the return type of |
Implement bulk creation of security group rules [1]. [1] https://docs.openstack.org/api-ref/network/v2/#bulk-create-security-group-rule
b1f8bbb to
6e6dbe5
Compare
|
FTR tested with this. package main
import (
"context"
"fmt"
"slices"
"strconv"
"github.com/gophercloud/gophercloud/v2/openstack"
"github.com/gophercloud/gophercloud/v2/openstack/config"
"github.com/gophercloud/gophercloud/v2/openstack/config/clouds"
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules"
)
const sgID = "5eddd6ae-bad2-4290-9361-5ae5552fc1e7"
func main() {
ctx := context.Background()
authOptions, endpointOptions, tlsConfig, err := clouds.Parse()
if err != nil {
panic(err)
}
providerClient, err := config.NewProviderClient(ctx, authOptions, config.WithTLSConfig(tlsConfig))
if err != nil {
panic(err)
}
client, err := openstack.NewNetworkV2(providerClient, endpointOptions)
if err != nil {
panic(err)
}
createOpts := make([]rules.CreateOpts, 1200)
for i := range createOpts {
createOpts[i] = rules.CreateOpts{
Direction: rules.DirIngress,
Description: "Rule number " + strconv.Itoa(i),
EtherType: rules.EtherType6,
SecGroupID: sgID,
PortRangeMax: i,
PortRangeMin: i,
Protocol: rules.ProtocolUDP,
}
}
sgs, err := rules.CreateBulk(ctx, client, createOpts).Extract()
if err != nil {
panic(err)
}
if want, got := len(createOpts), len(sgs); want != got {
fmt.Printf("ERROR: expected %d rules, got %d\n", want, got)
}
slices.SortFunc(sgs, func(a, b rules.SecGroupRule) int { return a.PortRangeMin - b.PortRangeMin })
for i := range sgs {
if want, got := createOpts[i].PortRangeMin, sgs[i].PortRangeMin; want != got {
fmt.Printf("Expected %d, got %d\n", want, got)
}
}
fmt.Printf("DONE. Successfully created %d rules and got %d returned rules.\n", len(createOpts), len(sgs))
} |
Implement bulk creation of security group rules [1].
[1] https://docs.openstack.org/api-ref/network/v2/#bulk-create-security-group-rule