yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/openstack/network.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  	"strings"
    21  	"time"
    22  
    23  	"github.com/pkg/errors"
    24  
    25  	"yunion.io/x/jsonutils"
    26  	"yunion.io/x/log"
    27  	"yunion.io/x/pkg/util/netutils"
    28  
    29  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  	"yunion.io/x/cloudmux/pkg/multicloud"
    32  	"yunion.io/x/onecloud/pkg/util/rbacutils"
    33  )
    34  
    35  type AllocationPool struct {
    36  	Start string `json:"start"`
    37  	End   string `json:"end"`
    38  }
    39  
    40  func (p AllocationPool) IsValid() bool {
    41  	for _, ip := range []string{p.Start, p.End} {
    42  		_, err := netutils.NewIPV4Addr(ip)
    43  		if err != nil {
    44  			return false
    45  		}
    46  	}
    47  	return true
    48  }
    49  
    50  func (p AllocationPool) Equals(s AllocationPool) bool {
    51  	return p.Start == s.Start && p.End == s.End
    52  }
    53  
    54  func (p AllocationPool) Contains(ip string) bool {
    55  	start, err := netutils.NewIPV4Addr(p.Start)
    56  	if err != nil {
    57  		return false
    58  	}
    59  	end, err := netutils.NewIPV4Addr(p.End)
    60  	if err != nil {
    61  		return false
    62  	}
    63  	addr, err := netutils.NewIPV4Addr(ip)
    64  	if err != nil {
    65  		return false
    66  	}
    67  	return netutils.NewIPV4AddrRange(start, end).Contains(addr)
    68  }
    69  
    70  type SNextLinks []SNextLink
    71  
    72  func (links SNextLinks) GetNextMark() string {
    73  	for _, link := range links {
    74  		if link.Rel == "next" && len(link.Href) > 0 {
    75  			href, err := url.Parse(link.Href)
    76  			if err != nil {
    77  				log.Errorf("failed parse next link %s error: %v", link.Href, err)
    78  				continue
    79  			}
    80  			marker := href.Query().Get("marker")
    81  			if len(marker) > 0 {
    82  				return marker
    83  			}
    84  		}
    85  	}
    86  	return ""
    87  }
    88  
    89  type SNextLink struct {
    90  	Href string
    91  	Rel  string
    92  }
    93  
    94  type SNetwork struct {
    95  	multicloud.SResourceBase
    96  	OpenStackTags
    97  	wire *SWire
    98  
    99  	Name            string
   100  	EnableDhcp      bool
   101  	NetworkId       string
   102  	SegmentId       string
   103  	ProjectId       string
   104  	TenantId        string
   105  	DnsNameservers  []string
   106  	AllocationPools []AllocationPool
   107  	HostRoutes      []string
   108  	IpVersion       int
   109  	GatewayIP       string
   110  	CIDR            string
   111  	Id              string
   112  	CreatedAt       time.Time
   113  	Description     string
   114  	Ipv6AddressMode string
   115  	Ipv6RaMode      string
   116  	RevisionNumber  int
   117  	ServiceTypes    []string
   118  	SubnetpoolId    string
   119  	Tags            []string
   120  	UpdatedAt       time.Time
   121  }
   122  
   123  func (network *SNetwork) GetId() string {
   124  	return network.Id
   125  }
   126  
   127  func (network *SNetwork) GetName() string {
   128  	if len(network.Name) > 0 {
   129  		return network.Name
   130  	}
   131  	return network.Id
   132  }
   133  
   134  func (network *SNetwork) GetGlobalId() string {
   135  	if len(network.AllocationPools) > 0 {
   136  		return fmt.Sprintf("%s|%s-%s", network.Id, network.AllocationPools[0].Start, network.AllocationPools[0].End)
   137  	}
   138  	return network.Id
   139  }
   140  
   141  func (network *SNetwork) IsEmulated() bool {
   142  	return false
   143  }
   144  
   145  func (network *SNetwork) GetStatus() string {
   146  	return api.NETWORK_STATUS_AVAILABLE
   147  }
   148  
   149  func (network *SNetwork) Delete() error {
   150  	nw, err := network.wire.vpc.region.GetNetwork(network.Id)
   151  	if err != nil {
   152  		return errors.Wrapf(err, "GetNetwork(%s)", network.Id)
   153  	}
   154  	if len(nw.AllocationPools) <= 1 || len(network.AllocationPools) == 0 {
   155  		return network.wire.vpc.region.DeleteNetwork(network.Id)
   156  	}
   157  	pools := []AllocationPool{}
   158  	for i := range nw.AllocationPools {
   159  		if nw.AllocationPools[i].Equals(network.AllocationPools[0]) {
   160  			continue
   161  		}
   162  		pools = append(pools, nw.AllocationPools[i])
   163  	}
   164  
   165  	params := map[string]interface{}{
   166  		"subnet": map[string]interface{}{
   167  			"allocation_pools": pools,
   168  		},
   169  	}
   170  	resource := fmt.Sprintf("/v2.0/subnets/%s", network.Id)
   171  	_, err = network.wire.vpc.region.vpcUpdate(resource, jsonutils.Marshal(params))
   172  	return err
   173  }
   174  
   175  func (region *SRegion) DeleteNetwork(networkId string) error {
   176  	resource := fmt.Sprintf("/v2.0/subnets/%s", networkId)
   177  	_, err := region.vpcDelete(resource)
   178  	return err
   179  }
   180  
   181  func (network *SNetwork) GetIWire() cloudprovider.ICloudWire {
   182  	return network.wire
   183  }
   184  
   185  func (network *SNetwork) GetAllocTimeoutSeconds() int {
   186  	return 120 // 2 minutes
   187  }
   188  
   189  func (network *SNetwork) GetGateway() string {
   190  	return network.GatewayIP
   191  }
   192  
   193  func (network *SNetwork) GetIpStart() string {
   194  	if len(network.AllocationPools) >= 1 {
   195  		return network.AllocationPools[0].Start
   196  	}
   197  	return ""
   198  }
   199  
   200  func (network *SNetwork) GetIpEnd() string {
   201  	if len(network.AllocationPools) >= 1 {
   202  		return network.AllocationPools[0].End
   203  	}
   204  	return ""
   205  }
   206  
   207  func (network *SNetwork) GetIPRange() netutils.IPV4AddrRange {
   208  	start, _ := netutils.NewIPV4Addr(network.GetIpStart())
   209  	end, _ := netutils.NewIPV4Addr(network.GetIpEnd())
   210  	return netutils.NewIPV4AddrRange(start, end)
   211  }
   212  
   213  func (network *SNetwork) Contains(ipAddr string) bool {
   214  	for _, pool := range network.AllocationPools {
   215  		if pool.Contains(ipAddr) {
   216  			return true
   217  		}
   218  	}
   219  	return false
   220  }
   221  
   222  func (network *SNetwork) GetIpMask() int8 {
   223  	pref, _ := netutils.NewIPV4Prefix(network.CIDR)
   224  	return pref.MaskLen
   225  }
   226  
   227  func (network *SNetwork) GetIsPublic() bool {
   228  	return network.wire.vpc.Shared
   229  }
   230  
   231  func (network *SNetwork) GetPublicScope() rbacutils.TRbacScope {
   232  	if network.wire.vpc.Shared {
   233  		return rbacutils.ScopeSystem
   234  	}
   235  	return rbacutils.ScopeNone
   236  }
   237  
   238  func (network *SNetwork) GetServerType() string {
   239  	return api.NETWORK_TYPE_GUEST
   240  }
   241  
   242  func getNetworkId(networkId string) (AllocationPool, string) {
   243  	pool := AllocationPool{}
   244  	if !strings.Contains(networkId, "|") {
   245  		return pool, networkId
   246  	}
   247  	info := strings.Split(networkId, "|")
   248  	networkId = info[0]
   249  	ipInfo := strings.Split(info[1], "-")
   250  	pool.Start, pool.End = ipInfo[0], ipInfo[1]
   251  	return pool, networkId
   252  }
   253  
   254  func (region *SRegion) GetNetwork(networkId string) (*SNetwork, error) {
   255  	var pool AllocationPool
   256  	pool, networkId = getNetworkId(networkId)
   257  	resource := fmt.Sprintf("/v2.0/subnets/%s", networkId)
   258  	resp, err := region.vpcGet(resource)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	network := &SNetwork{}
   263  	err = resp.Unmarshal(network, "subnet")
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	if len(pool.Start) == 0 && len(pool.End) == 0 {
   268  		return network, nil
   269  	}
   270  	for _, _pool := range network.AllocationPools {
   271  		if _pool.Equals(pool) {
   272  			network.AllocationPools = []AllocationPool{pool}
   273  			return network, nil
   274  		}
   275  	}
   276  	return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", networkId)
   277  }
   278  
   279  func (region *SRegion) GetNetworks(vpcId string) ([]SNetwork, error) {
   280  	resource := "/v2.0/subnets"
   281  	networks := []SNetwork{}
   282  	query := url.Values{}
   283  	if len(vpcId) > 0 {
   284  		query.Set("network_id", vpcId)
   285  	}
   286  	for {
   287  		resp, err := region.vpcList(resource, query)
   288  		if err != nil {
   289  			return nil, errors.Wrap(err, "vpcList")
   290  		}
   291  
   292  		part := struct {
   293  			Subnets      []SNetwork
   294  			SubnetsLinks SNextLinks
   295  		}{}
   296  
   297  		err = resp.Unmarshal(&part)
   298  		if err != nil {
   299  			return nil, errors.Wrap(err, "resp.Unmarshal")
   300  		}
   301  
   302  		networks = append(networks, part.Subnets...)
   303  		marker := part.SubnetsLinks.GetNextMark()
   304  		if len(marker) == 0 {
   305  			break
   306  		}
   307  		query.Set("marker", marker)
   308  	}
   309  	return networks, nil
   310  }
   311  
   312  func (network *SNetwork) Refresh() error {
   313  	_network, err := network.wire.vpc.region.GetNetwork(network.Id)
   314  	if err != nil {
   315  		return err
   316  	}
   317  	return jsonutils.Update(network, _network)
   318  }
   319  
   320  func (region *SRegion) CreateNetwork(vpcId string, projectId, name string, cidr string, desc string) (*SNetwork, error) {
   321  	params := map[string]map[string]interface{}{
   322  		"subnet": {
   323  			"name":        name,
   324  			"network_id":  vpcId,
   325  			"cidr":        cidr,
   326  			"description": desc,
   327  			"ip_version":  4,
   328  		},
   329  	}
   330  	if len(projectId) > 0 {
   331  		params["subnet"]["project_id"] = projectId
   332  	}
   333  	resp, err := region.vpcPost("/v2.0/subnets", params)
   334  	if err != nil {
   335  		return nil, errors.Wrap(err, "vpcPost")
   336  	}
   337  	network := &SNetwork{}
   338  	err = resp.Unmarshal(network, "subnet")
   339  	if err != nil {
   340  		return nil, errors.Wrap(err, "resp.Unmarshal")
   341  	}
   342  	return network, nil
   343  }
   344  
   345  func (network *SNetwork) GetProjectId() string {
   346  	return network.TenantId
   347  }