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  }