yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/openstack/securitygroup.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package openstack 16 17 import ( 18 "fmt" 19 "net/url" 20 "time" 21 22 "yunion.io/x/jsonutils" 23 "yunion.io/x/log" 24 "yunion.io/x/pkg/errors" 25 "yunion.io/x/pkg/util/secrules" 26 "yunion.io/x/pkg/utils" 27 28 "yunion.io/x/cloudmux/pkg/cloudprovider" 29 "yunion.io/x/onecloud/pkg/httperrors" 30 "yunion.io/x/cloudmux/pkg/multicloud" 31 "yunion.io/x/onecloud/pkg/util/httputils" 32 ) 33 34 const ( 35 SECGROUP_NOT_SUPPORT = "openstack_skip_security_group" 36 ) 37 38 type SSecurityGroupRule struct { 39 Direction string 40 Ethertype string 41 Id string 42 PortRangeMax int 43 PortRangeMin int 44 Protocol string 45 RemoteGroupId string 46 RemoteIpPrefix string 47 SecurityGroupId string 48 ProjectId string 49 RevisionNumber int 50 Tags []string 51 TenantId string 52 CreatedAt time.Time 53 UpdatedAt time.Time 54 Description string 55 } 56 57 type SSecurityGroup struct { 58 multicloud.SSecurityGroup 59 OpenStackTags 60 region *SRegion 61 62 Description string 63 Id string 64 Name string 65 SecurityGroupRules []SSecurityGroupRule 66 ProjectId string 67 RevisionNumber int 68 CreatedAt time.Time 69 UpdatedAt time.Time 70 Tags []string 71 TenantId string 72 } 73 74 func (region *SRegion) GetSecurityGroup(secgroupId string) (*SSecurityGroup, error) { 75 resource := "/v2.0/security-groups/" + secgroupId 76 resp, err := region.vpcGet(resource) 77 if err != nil { 78 return nil, errors.Wrap(err, "vpcGet") 79 } 80 secgroup := &SSecurityGroup{region: region} 81 err = resp.Unmarshal(secgroup, "security_group") 82 if err != nil { 83 return nil, errors.Wrap(err, "resp.Unmarshal") 84 } 85 return secgroup, nil 86 } 87 88 func (region *SRegion) GetSecurityGroups(projectId, name string) ([]SSecurityGroup, error) { 89 secgroups := []SSecurityGroup{} 90 resource := "/v2.0/security-groups" 91 query := url.Values{} 92 if len(name) > 0 { 93 query.Set("name", name) 94 } 95 if len(projectId) > 0 { 96 query.Set("project_id", projectId) 97 } 98 for { 99 resp, err := region.vpcList(resource, query) 100 if err != nil { 101 return nil, errors.Wrap(err, "vpcList") 102 } 103 part := struct { 104 SecurityGroups []SSecurityGroup 105 SecurityGroupsLinks SNextLinks 106 }{} 107 err = resp.Unmarshal(&part) 108 if err != nil { 109 return nil, errors.Wrap(err, "resp.Unmarshal") 110 } 111 secgroups = append(secgroups, part.SecurityGroups...) 112 marker := part.SecurityGroupsLinks.GetNextMark() 113 if len(marker) == 0 { 114 break 115 } 116 query.Set("marker", marker) 117 } 118 return secgroups, nil 119 } 120 121 func (secgroup *SSecurityGroup) GetVpcId() string { 122 return "normal" 123 } 124 125 func (secgroup *SSecurityGroup) GetId() string { 126 return secgroup.Id 127 } 128 129 func (secgroup *SSecurityGroup) GetGlobalId() string { 130 return secgroup.Id 131 } 132 133 func (secgroup *SSecurityGroup) GetDescription() string { 134 return secgroup.Description 135 } 136 137 func (secgroup *SSecurityGroup) GetName() string { 138 if len(secgroup.Name) > 0 { 139 return secgroup.Name 140 } 141 return secgroup.Id 142 } 143 144 func (secgrouprule *SSecurityGroupRule) toRules() ([]cloudprovider.SecurityRule, error) { 145 rules := []cloudprovider.SecurityRule{} 146 // 暂时忽略IPv6安全组规则,忽略远端也是安全组的规则 147 if secgrouprule.Ethertype != "IPv4" || len(secgrouprule.RemoteGroupId) > 0 { 148 return rules, fmt.Errorf("ethertype: %s remoteGroupId: %s", secgrouprule.Ethertype, secgrouprule.RemoteGroupId) 149 } 150 rule := cloudprovider.SecurityRule{ 151 ExternalId: secgrouprule.Id, 152 SecurityRule: secrules.SecurityRule{ 153 Direction: secrules.DIR_IN, 154 Action: secrules.SecurityRuleAllow, 155 Description: secgrouprule.Description, 156 }, 157 } 158 if utils.IsInStringArray(secgrouprule.Protocol, []string{"any", "-1", ""}) { 159 rule.Protocol = secrules.PROTO_ANY 160 } else if utils.IsInStringArray(secgrouprule.Protocol, []string{"6", "tcp"}) { 161 rule.Protocol = secrules.PROTO_TCP 162 } else if utils.IsInStringArray(secgrouprule.Protocol, []string{"17", "udp"}) { 163 rule.Protocol = secrules.PROTO_UDP 164 } else if utils.IsInStringArray(secgrouprule.Protocol, []string{"1", "icmp"}) { 165 rule.Protocol = secrules.PROTO_ICMP 166 } else { 167 return rules, errors.Wrap(httperrors.ErrUnsupportedProtocol, secgrouprule.Protocol) 168 } 169 if secgrouprule.Direction == "egress" { 170 rule.Direction = secrules.DIR_OUT 171 } 172 if len(secgrouprule.RemoteIpPrefix) == 0 { 173 secgrouprule.RemoteIpPrefix = "0.0.0.0/0" 174 } 175 176 rule.ParseCIDR(secgrouprule.RemoteIpPrefix) 177 if secgrouprule.PortRangeMax > 0 && secgrouprule.PortRangeMin > 0 { 178 if secgrouprule.PortRangeMax == secgrouprule.PortRangeMin { 179 rule.Ports = []int{secgrouprule.PortRangeMax} 180 } else { 181 rule.PortStart = secgrouprule.PortRangeMin 182 rule.PortEnd = secgrouprule.PortRangeMax 183 } 184 } 185 err := rule.ValidateRule() 186 if err != nil && err != secrules.ErrInvalidPriority { 187 return rules, errors.Wrap(err, "rule.ValidateRule") 188 } 189 return []cloudprovider.SecurityRule{rule}, nil 190 } 191 192 func (secgroup *SSecurityGroup) GetRules() ([]cloudprovider.SecurityRule, error) { 193 rules := []cloudprovider.SecurityRule{} 194 for _, rule := range secgroup.SecurityGroupRules { 195 subRules, err := rule.toRules() 196 if err != nil { 197 log.Errorf("failed to convert rule %s for secgroup %s(%s) error: %v", rule.Id, secgroup.Name, secgroup.Id, err) 198 continue 199 } 200 rules = append(rules, subRules...) 201 } 202 return rules, nil 203 } 204 205 func (secgroup *SSecurityGroup) GetStatus() string { 206 return "" 207 } 208 209 func (secgroup *SSecurityGroup) IsEmulated() bool { 210 return false 211 } 212 213 func (secgroup *SSecurityGroup) Refresh() error { 214 new, err := secgroup.region.GetSecurityGroup(secgroup.Id) 215 if err != nil { 216 return err 217 } 218 return jsonutils.Update(secgroup, new) 219 } 220 221 func (region *SRegion) delSecurityGroupRule(ruleId string) error { 222 resource := "/v2.0/security-group-rules/" + ruleId 223 _, err := region.vpcDelete(resource) 224 return err 225 } 226 227 func (region *SRegion) addSecurityGroupRules(secgroupId string, rule cloudprovider.SecurityRule) error { 228 direction := "ingress" 229 if rule.Direction == secrules.SecurityRuleEgress { 230 direction = "egress" 231 } 232 233 if rule.Protocol == secrules.PROTO_ANY { 234 rule.Protocol = "" 235 } 236 237 ruleInfo := map[string]interface{}{ 238 "direction": direction, 239 "security_group_id": secgroupId, 240 "remote_ip_prefix": rule.IPNet.String(), 241 } 242 if len(rule.Protocol) > 0 { 243 ruleInfo["protocol"] = rule.Protocol 244 } 245 246 params := map[string]map[string]interface{}{ 247 "security_group_rule": ruleInfo, 248 } 249 if len(rule.Ports) > 0 { 250 for _, port := range rule.Ports { 251 params["security_group_rule"]["port_range_max"] = port 252 params["security_group_rule"]["port_range_min"] = port 253 resource := "/v2.0/security-group-rules" 254 _, err := region.vpcPost(resource, params) 255 if err != nil { 256 return errors.Wrap(err, "vpcPost") 257 } 258 } 259 return nil 260 } 261 if rule.PortEnd > 0 && rule.PortStart > 0 { 262 params["security_group_rule"]["port_range_min"] = rule.PortStart 263 params["security_group_rule"]["port_range_max"] = rule.PortEnd 264 } 265 _, err := region.vpcPost("/v2.0/security-group-rules", params) 266 return err 267 } 268 269 func (region *SRegion) DeleteSecurityGroup(secGroupId string) error { 270 resource := "/v2.0/security-groups/" + secGroupId 271 _, err := region.vpcDelete(resource) 272 return err 273 } 274 275 func (secgroup *SSecurityGroup) Delete() error { 276 return secgroup.region.DeleteSecurityGroup(secgroup.Id) 277 } 278 279 func (region *SRegion) CreateSecurityGroup(projectId, name, description string) (*SSecurityGroup, error) { 280 params := map[string]map[string]interface{}{ 281 "security_group": { 282 "name": name, 283 "description": description, 284 }, 285 } 286 if len(projectId) > 0 { 287 params["security_group"]["project_id"] = projectId 288 } 289 resp, err := region.vpcPost("/v2.0/security-groups", params) 290 if err != nil { 291 return nil, errors.Wrap(err, "vpcPost") 292 } 293 secgroup := &SSecurityGroup{region: region} 294 err = resp.Unmarshal(secgroup, "security_group") 295 if err != nil { 296 return nil, errors.Wrap(err, "resp.Unmarshal") 297 } 298 return secgroup, nil 299 } 300 301 func (secgroup *SSecurityGroup) GetProjectId() string { 302 return secgroup.TenantId 303 } 304 305 func (secgroup *SSecurityGroup) SyncRules(common, inAdds, outAdds, inDels, outDels []cloudprovider.SecurityRule) error { 306 for _, r := range append(inDels, outDels...) { 307 err := secgroup.region.delSecurityGroupRule(r.ExternalId) 308 if err != nil { 309 return errors.Wrapf(err, "delSecurityGroupRule(%s)", r.ExternalId) 310 } 311 } 312 for _, r := range append(inAdds, outAdds...) { 313 err := secgroup.region.addSecurityGroupRules(secgroup.Id, r) 314 if err != nil { 315 if jsonError, ok := err.(*httputils.JSONClientError); ok { 316 if jsonError.Class == "SecurityGroupRuleExists" { 317 continue 318 } 319 } 320 return errors.Wrapf(err, "addSecgroupRules(%s)", r.String()) 321 } 322 } 323 return nil 324 }