blob: 120dcae3c8c0889a943ed2c1570003f193f1ab29 [file] [log] [blame]
Jamie Hannaford924c09d2014-11-19 12:05:38 +01001package secgroups
2
3import (
Jamie Hannaford8badf1e2014-11-19 14:39:26 +01004 "errors"
Denis Zhdanov98c77d02016-01-24 18:10:09 +01005 "strings"
Jamie Hannaford8badf1e2014-11-19 14:39:26 +01006
Jamie Hannaford924c09d2014-11-19 12:05:38 +01007 "github.com/rackspace/gophercloud"
8 "github.com/rackspace/gophercloud/pagination"
9)
10
Jamie Hannaford19151792014-11-19 12:46:47 +010011func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
Jamie Hannaford924c09d2014-11-19 12:05:38 +010012 createPage := func(r pagination.PageResult) pagination.Page {
13 return SecurityGroupPage{pagination.SinglePageBase(r)}
14 }
15
Jamie Hannaford19151792014-11-19 12:46:47 +010016 return pagination.NewPager(client, url, createPage)
17}
18
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010019// List will return a collection of all the security groups for a particular
20// tenant.
Jamie Hannaford19151792014-11-19 12:46:47 +010021func List(client *gophercloud.ServiceClient) pagination.Pager {
22 return commonList(client, rootURL(client))
23}
24
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010025// ListByServer will return a collection of all the security groups which are
26// associated with a particular server.
Jamie Hannaford19151792014-11-19 12:46:47 +010027func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
28 return commonList(client, listByServerURL(client, serverID))
Jamie Hannaford924c09d2014-11-19 12:05:38 +010029}
Jamie Hannaforda493e642014-11-19 12:40:30 +010030
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010031// GroupOpts is the underlying struct responsible for creating or updating
32// security groups. It therefore represents the mutable attributes of a
33// security group.
Jamie Hannaford30c74662014-11-19 15:37:34 +010034type GroupOpts struct {
Jamie Hannaford415ff942014-11-25 15:25:57 +010035 // Required - the name of your security group.
Jamie Hannaford0e750962014-11-24 16:04:38 +010036 Name string `json:"name"`
Jamie Hannaforda493e642014-11-19 12:40:30 +010037
Jamie Hannaford415ff942014-11-25 15:25:57 +010038 // Required - the description of your security group.
Jamie Hannaford0e750962014-11-24 16:04:38 +010039 Description string `json:"description"`
Jamie Hannaforda493e642014-11-19 12:40:30 +010040}
41
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010042// CreateOpts is the struct responsible for creating a security group.
Jamie Hannaford30c74662014-11-19 15:37:34 +010043type CreateOpts GroupOpts
44
Jamie Hannaford04abbc72014-11-21 11:27:57 +010045// CreateOptsBuilder builds the create options into a serializable format.
46type CreateOptsBuilder interface {
47 ToSecGroupCreateMap() (map[string]interface{}, error)
48}
49
Jamie Hannaford0e750962014-11-24 16:04:38 +010050var (
51 errName = errors.New("Name is a required field")
52 errDesc = errors.New("Description is a required field")
53)
54
Jamie Hannaford04abbc72014-11-21 11:27:57 +010055// ToSecGroupCreateMap builds the create options into a serializable format.
56func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
57 sg := make(map[string]interface{})
58
Jamie Hannaford0e750962014-11-24 16:04:38 +010059 if opts.Name == "" {
60 return sg, errName
Jamie Hannaford04abbc72014-11-21 11:27:57 +010061 }
Jamie Hannaford0e750962014-11-24 16:04:38 +010062 if opts.Description == "" {
63 return sg, errDesc
Jamie Hannaford04abbc72014-11-21 11:27:57 +010064 }
65
Jamie Hannaford0e750962014-11-24 16:04:38 +010066 sg["name"] = opts.Name
67 sg["description"] = opts.Description
68
Jamie Hannaford04abbc72014-11-21 11:27:57 +010069 return map[string]interface{}{"security_group": sg}, nil
70}
71
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010072// Create will create a new security group.
Jamie Hannaford04abbc72014-11-21 11:27:57 +010073func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
Jamie Hannaforda493e642014-11-19 12:40:30 +010074 var result CreateResult
75
Jamie Hannaford04abbc72014-11-21 11:27:57 +010076 reqBody, err := opts.ToSecGroupCreateMap()
77 if err != nil {
78 result.Err = err
79 return result
80 }
Jamie Hannaforda493e642014-11-19 12:40:30 +010081
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +010082 _, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
83 OkCodes: []int{200},
Jamie Hannaforda493e642014-11-19 12:40:30 +010084 })
85
86 return result
87}
Jamie Hannafordb38dd312014-11-19 13:02:11 +010088
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010089// UpdateOpts is the struct responsible for updating an existing security group.
Jamie Hannaford30c74662014-11-19 15:37:34 +010090type UpdateOpts GroupOpts
91
Jamie Hannaford04abbc72014-11-21 11:27:57 +010092// UpdateOptsBuilder builds the update options into a serializable format.
93type UpdateOptsBuilder interface {
94 ToSecGroupUpdateMap() (map[string]interface{}, error)
95}
96
97// ToSecGroupUpdateMap builds the update options into a serializable format.
98func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
99 sg := make(map[string]interface{})
100
Jamie Hannaford0e750962014-11-24 16:04:38 +0100101 if opts.Name == "" {
102 return sg, errName
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100103 }
Jamie Hannaford0e750962014-11-24 16:04:38 +0100104 if opts.Description == "" {
105 return sg, errDesc
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100106 }
107
Jamie Hannaford0e750962014-11-24 16:04:38 +0100108 sg["name"] = opts.Name
109 sg["description"] = opts.Description
110
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100111 return map[string]interface{}{"security_group": sg}, nil
112}
113
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100114// Update will modify the mutable properties of a security group, notably its
115// name and description.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100116func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
Jamie Hannaford30c74662014-11-19 15:37:34 +0100117 var result UpdateResult
118
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100119 reqBody, err := opts.ToSecGroupUpdateMap()
120 if err != nil {
121 result.Err = err
122 return result
123 }
Jamie Hannaford30c74662014-11-19 15:37:34 +0100124
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100125 _, result.Err = client.Put(resourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
126 OkCodes: []int{200},
Jamie Hannaford30c74662014-11-19 15:37:34 +0100127 })
128
129 return result
130}
131
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100132// Get will return details for a particular security group.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100133func Get(client *gophercloud.ServiceClient, id string) GetResult {
Jamie Hannafordb38dd312014-11-19 13:02:11 +0100134 var result GetResult
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100135 _, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
Jamie Hannafordb38dd312014-11-19 13:02:11 +0100136 return result
137}
Jamie Hannafordd276e612014-11-19 13:56:28 +0100138
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100139// Delete will permanently delete a security group from the project.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100140func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
Jamie Hannafordd276e612014-11-19 13:56:28 +0100141 var result gophercloud.ErrResult
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100142 _, result.Err = client.Delete(resourceURL(client, id), nil)
Jamie Hannafordd276e612014-11-19 13:56:28 +0100143 return result
144}
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100145
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100146// CreateRuleOpts represents the configuration for adding a new rule to an
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100147// existing security group.
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100148type CreateRuleOpts struct {
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100149 // Required - the ID of the group that this rule will be added to.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100150 ParentGroupID string `json:"parent_group_id"`
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100151
152 // Required - the lower bound of the port range that will be opened.
153 FromPort int `json:"from_port"`
154
155 // Required - the upper bound of the port range that will be opened.
156 ToPort int `json:"to_port"`
157
158 // Required - the protocol type that will be allowed, e.g. TCP.
159 IPProtocol string `json:"ip_protocol"`
160
161 // ONLY required if FromGroupID is blank. This represents the IP range that
162 // will be the source of network traffic to your security group. Use
163 // 0.0.0.0/0 to allow all IP addresses.
164 CIDR string `json:"cidr,omitempty"`
165
166 // ONLY required if CIDR is blank. This value represents the ID of a group
167 // that forwards traffic to the parent group. So, instead of accepting
168 // network traffic from an entire IP range, you can instead refine the
169 // inbound source by an existing security group.
170 FromGroupID string `json:"group_id,omitempty"`
171}
172
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100173// CreateRuleOptsBuilder builds the create rule options into a serializable format.
174type CreateRuleOptsBuilder interface {
175 ToRuleCreateMap() (map[string]interface{}, error)
176}
177
178// ToRuleCreateMap builds the create rule options into a serializable format.
179func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
180 rule := make(map[string]interface{})
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100181
Jamie Hannaford2f226172014-11-25 11:52:25 +0100182 if opts.ParentGroupID == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100183 return rule, errors.New("A ParentGroupID must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100184 }
Denys Zhdanovaf498472016-01-19 11:34:21 +0100185 if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100186 return rule, errors.New("A FromPort must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100187 }
Denys Zhdanovaf498472016-01-19 11:34:21 +0100188 if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100189 return rule, errors.New("A ToPort must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100190 }
191 if opts.IPProtocol == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100192 return rule, errors.New("A IPProtocol must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100193 }
194 if opts.CIDR == "" && opts.FromGroupID == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100195 return rule, errors.New("A CIDR or FromGroupID must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100196 }
197
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100198 rule["parent_group_id"] = opts.ParentGroupID
199 rule["from_port"] = opts.FromPort
200 rule["to_port"] = opts.ToPort
201 rule["ip_protocol"] = opts.IPProtocol
202
203 if opts.CIDR != "" {
204 rule["cidr"] = opts.CIDR
205 }
206 if opts.FromGroupID != "" {
Long Nguyenca51f012015-02-16 15:52:22 -0500207 rule["group_id"] = opts.FromGroupID
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100208 }
209
210 return map[string]interface{}{"security_group_rule": rule}, nil
211}
212
213// CreateRule will add a new rule to an existing security group (whose ID is
214// specified in CreateRuleOpts). You have the option of controlling inbound
215// traffic from either an IP range (CIDR) or from another security group.
216func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) CreateRuleResult {
217 var result CreateRuleResult
218
219 reqBody, err := opts.ToRuleCreateMap()
220 if err != nil {
221 result.Err = err
222 return result
223 }
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100224
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100225 _, result.Err = client.Post(rootRuleURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
226 OkCodes: []int{200},
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100227 })
228
229 return result
230}
Jamie Hannaford61f81ca2014-11-19 14:44:33 +0100231
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100232// DeleteRule will permanently delete a rule from a security group.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100233func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
Jamie Hannaford61f81ca2014-11-19 14:44:33 +0100234 var result gophercloud.ErrResult
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100235 _, result.Err = client.Delete(resourceRuleURL(client, id), nil)
Jamie Hannaford61f81ca2014-11-19 14:44:33 +0100236 return result
237}
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100238
239func actionMap(prefix, groupName string) map[string]map[string]string {
240 return map[string]map[string]string{
241 prefix + "SecurityGroup": map[string]string{"name": groupName},
242 }
243}
244
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100245// AddServerToGroup will associate a server and a security group, enforcing the
246// rules of the group on the server.
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100247func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
248 var result gophercloud.ErrResult
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100249 _, result.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &result.Body, nil)
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100250 return result
251}
252
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100253// RemoveServerFromGroup will disassociate a server from a security group.
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100254func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
255 var result gophercloud.ErrResult
Jamie Hannaford6a3a78f2015-03-24 14:56:12 +0100256 _, result.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &result.Body, nil)
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100257 return result
258}