github.com/vmware/govmomi@v0.37.1/simulator/ip_pool_manager.go (about)

     1  /*
     2  Copyright (c) 2017 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package simulator
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"net"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"github.com/vmware/govmomi/vim25/methods"
    27  	"github.com/vmware/govmomi/vim25/mo"
    28  	"github.com/vmware/govmomi/vim25/soap"
    29  	"github.com/vmware/govmomi/vim25/types"
    30  )
    31  
    32  var ipPool = MustNewIpPool(&types.IpPool{
    33  	Id:                     1,
    34  	Name:                   "ip-pool",
    35  	AvailableIpv4Addresses: 250,
    36  	AvailableIpv6Addresses: 250,
    37  	AllocatedIpv6Addresses: 0,
    38  	AllocatedIpv4Addresses: 0,
    39  	Ipv4Config: &types.IpPoolIpPoolConfigInfo{
    40  		Netmask:       "10.10.10.255",
    41  		Gateway:       "10.10.10.1",
    42  		SubnetAddress: "10.10.10.0",
    43  		Range:         "10.10.10.2#250",
    44  	},
    45  	Ipv6Config: &types.IpPoolIpPoolConfigInfo{
    46  		Netmask:       "2001:4860:0:2001::ff",
    47  		Gateway:       "2001:4860:0:2001::1",
    48  		SubnetAddress: "2001:4860:0:2001::0",
    49  		Range:         "2001:4860:0:2001::2#250",
    50  	},
    51  })
    52  
    53  // IpPoolManager implements a simple IP Pool manager in which all pools are shared
    54  // across different datacenters.
    55  type IpPoolManager struct {
    56  	mo.IpPoolManager
    57  
    58  	pools      map[int32]*IpPool
    59  	nextPoolId int32
    60  }
    61  
    62  func (m *IpPoolManager) init(*Registry) {
    63  	m.pools = map[int32]*IpPool{
    64  		1: ipPool,
    65  	}
    66  	m.nextPoolId = 2
    67  }
    68  
    69  func (m *IpPoolManager) CreateIpPool(req *types.CreateIpPool) soap.HasFault {
    70  	body := &methods.CreateIpPoolBody{}
    71  	id := m.nextPoolId
    72  
    73  	var err error
    74  	m.pools[id], err = NewIpPool(&req.Pool)
    75  	if err != nil {
    76  		body.Fault_ = Fault("", &types.RuntimeFault{})
    77  		return body
    78  	}
    79  
    80  	m.nextPoolId++
    81  
    82  	body.Res = &types.CreateIpPoolResponse{
    83  		Returnval: id,
    84  	}
    85  
    86  	return body
    87  }
    88  
    89  func (m *IpPoolManager) DestroyIpPool(req *types.DestroyIpPool) soap.HasFault {
    90  	delete(m.pools, req.Id)
    91  
    92  	return &methods.DestroyIpPoolBody{
    93  		Res: &types.DestroyIpPoolResponse{},
    94  	}
    95  }
    96  
    97  func (m *IpPoolManager) QueryIpPools(req *types.QueryIpPools) soap.HasFault {
    98  	pools := []types.IpPool{}
    99  
   100  	for i := int32(1); i < m.nextPoolId; i++ {
   101  		if p, ok := m.pools[i]; ok {
   102  			pools = append(pools, *p.config)
   103  		}
   104  	}
   105  
   106  	return &methods.QueryIpPoolsBody{
   107  		Res: &types.QueryIpPoolsResponse{
   108  			Returnval: pools,
   109  		},
   110  	}
   111  }
   112  
   113  func (m *IpPoolManager) UpdateIpPool(req *types.UpdateIpPool) soap.HasFault {
   114  	body := &methods.UpdateIpPoolBody{}
   115  
   116  	var pool *IpPool
   117  	var err error
   118  	var ok bool
   119  
   120  	if pool, ok = m.pools[req.Pool.Id]; !ok {
   121  		body.Fault_ = Fault("", &types.NotFoundFault{})
   122  		return body
   123  	}
   124  
   125  	if pool.config.AllocatedIpv4Addresses+pool.config.AllocatedIpv6Addresses != 0 {
   126  		body.Fault_ = Fault("update a pool has been used is not supported", &types.RuntimeFault{})
   127  		return body
   128  	}
   129  
   130  	m.pools[req.Pool.Id], err = NewIpPool(&req.Pool)
   131  	if err != nil {
   132  		body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
   133  		return body
   134  	}
   135  
   136  	body.Res = &types.UpdateIpPoolResponse{}
   137  
   138  	return body
   139  }
   140  
   141  func (m *IpPoolManager) AllocateIpv4Address(req *types.AllocateIpv4Address) soap.HasFault {
   142  	body := &methods.AllocateIpv4AddressBody{}
   143  
   144  	pool, ok := m.pools[req.PoolId]
   145  	if !ok {
   146  		body.Fault_ = Fault("", &types.InvalidArgument{})
   147  		return body
   148  	}
   149  
   150  	ip, err := pool.AllocateIPv4(req.AllocationId)
   151  	if err != nil {
   152  		body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
   153  		return body
   154  	}
   155  
   156  	body.Res = &types.AllocateIpv4AddressResponse{
   157  		Returnval: ip,
   158  	}
   159  
   160  	return body
   161  }
   162  
   163  func (m *IpPoolManager) AllocateIpv6Address(req *types.AllocateIpv6Address) soap.HasFault {
   164  	body := &methods.AllocateIpv6AddressBody{}
   165  
   166  	pool, ok := m.pools[req.PoolId]
   167  	if !ok {
   168  		body.Fault_ = Fault("", &types.InvalidArgument{})
   169  		return body
   170  	}
   171  
   172  	ip, err := pool.AllocateIpv6(req.AllocationId)
   173  	if err != nil {
   174  		body.Fault_ = Fault(err.Error(), &types.RuntimeFault{})
   175  		return body
   176  	}
   177  
   178  	body.Res = &types.AllocateIpv6AddressResponse{
   179  		Returnval: ip,
   180  	}
   181  
   182  	return body
   183  }
   184  
   185  func (m *IpPoolManager) ReleaseIpAllocation(req *types.ReleaseIpAllocation) soap.HasFault {
   186  	body := &methods.ReleaseIpAllocationBody{}
   187  
   188  	pool, ok := m.pools[req.PoolId]
   189  	if !ok {
   190  		body.Fault_ = Fault("", &types.InvalidArgument{})
   191  		return body
   192  	}
   193  
   194  	pool.ReleaseIpv4(req.AllocationId)
   195  	pool.ReleaseIpv6(req.AllocationId)
   196  
   197  	body.Res = &types.ReleaseIpAllocationResponse{}
   198  
   199  	return body
   200  }
   201  
   202  func (m *IpPoolManager) QueryIPAllocations(req *types.QueryIPAllocations) soap.HasFault {
   203  	body := &methods.QueryIPAllocationsBody{}
   204  
   205  	pool, ok := m.pools[req.PoolId]
   206  	if !ok {
   207  		body.Fault_ = Fault("", &types.InvalidArgument{})
   208  		return body
   209  	}
   210  
   211  	body.Res = &types.QueryIPAllocationsResponse{}
   212  
   213  	ipv4, ok := pool.ipv4Allocation[req.ExtensionKey]
   214  	if ok {
   215  		body.Res.Returnval = append(body.Res.Returnval, types.IpPoolManagerIpAllocation{
   216  			IpAddress:    ipv4,
   217  			AllocationId: req.ExtensionKey,
   218  		})
   219  	}
   220  
   221  	ipv6, ok := pool.ipv6Allocation[req.ExtensionKey]
   222  	if ok {
   223  		body.Res.Returnval = append(body.Res.Returnval, types.IpPoolManagerIpAllocation{
   224  			IpAddress:    ipv6,
   225  			AllocationId: req.ExtensionKey,
   226  		})
   227  	}
   228  
   229  	return body
   230  }
   231  
   232  var (
   233  	errNoIpAvailable     = errors.New("no ip address available")
   234  	errInvalidAllocation = errors.New("allocation id not recognized")
   235  )
   236  
   237  type IpPool struct {
   238  	config         *types.IpPool
   239  	ipv4Allocation map[string]string
   240  	ipv6Allocation map[string]string
   241  	ipv4Pool       []string
   242  	ipv6Pool       []string
   243  }
   244  
   245  func MustNewIpPool(config *types.IpPool) *IpPool {
   246  	pool, err := NewIpPool(config)
   247  	if err != nil {
   248  		panic(err)
   249  	}
   250  
   251  	return pool
   252  }
   253  
   254  func NewIpPool(config *types.IpPool) (*IpPool, error) {
   255  	pool := &IpPool{
   256  		config:         config,
   257  		ipv4Allocation: make(map[string]string),
   258  		ipv6Allocation: make(map[string]string),
   259  	}
   260  
   261  	return pool, pool.init()
   262  }
   263  
   264  func (p *IpPool) init() error {
   265  	// IPv4 range
   266  	if p.config.Ipv4Config != nil {
   267  		ranges := strings.Split(p.config.Ipv4Config.Range, ",")
   268  		for _, r := range ranges {
   269  			sp := strings.Split(r, "#")
   270  			if len(sp) != 2 {
   271  				return fmt.Errorf("format of range should be ip#number; got %q", r)
   272  			}
   273  
   274  			ip := net.ParseIP(strings.TrimSpace(sp[0])).To4()
   275  			if ip == nil {
   276  				return fmt.Errorf("bad ip format: %q", sp[0])
   277  			}
   278  
   279  			length, err := strconv.Atoi(sp[1])
   280  			if err != nil {
   281  				return err
   282  			}
   283  
   284  			for i := 0; i < length; i++ {
   285  				p.ipv4Pool = append(p.ipv4Pool, net.IPv4(ip[0], ip[1], ip[2], ip[3]+byte(i)).String())
   286  			}
   287  		}
   288  	}
   289  
   290  	// IPv6 range
   291  	if p.config.Ipv6Config != nil {
   292  		ranges := strings.Split(p.config.Ipv6Config.Range, ",")
   293  		for _, r := range ranges {
   294  			sp := strings.Split(r, "#")
   295  			if len(sp) != 2 {
   296  				return fmt.Errorf("format of range should be ip#number; got %q", r)
   297  			}
   298  
   299  			ip := net.ParseIP(strings.TrimSpace(sp[0])).To16()
   300  			if ip == nil {
   301  				return fmt.Errorf("bad ip format: %q", sp[0])
   302  			}
   303  
   304  			length, err := strconv.Atoi(sp[1])
   305  			if err != nil {
   306  				return err
   307  			}
   308  
   309  			for i := 0; i < length; i++ {
   310  				var ipv6 [16]byte
   311  				copy(ipv6[:], ip)
   312  				ipv6[15] += byte(i)
   313  				p.ipv6Pool = append(p.ipv6Pool, net.IP(ipv6[:]).String())
   314  			}
   315  		}
   316  	}
   317  
   318  	return nil
   319  }
   320  
   321  func (p *IpPool) AllocateIPv4(allocation string) (string, error) {
   322  	if ip, ok := p.ipv4Allocation[allocation]; ok {
   323  		return ip, nil
   324  	}
   325  
   326  	l := len(p.ipv4Pool)
   327  	if l == 0 {
   328  		return "", errNoIpAvailable
   329  	}
   330  
   331  	ip := p.ipv4Pool[l-1]
   332  
   333  	p.config.AvailableIpv4Addresses--
   334  	p.config.AllocatedIpv4Addresses++
   335  	p.ipv4Pool = p.ipv4Pool[:l-1]
   336  	p.ipv4Allocation[allocation] = ip
   337  
   338  	return ip, nil
   339  }
   340  
   341  func (p *IpPool) ReleaseIpv4(allocation string) error {
   342  	ip, ok := p.ipv4Allocation[allocation]
   343  	if !ok {
   344  		return errInvalidAllocation
   345  	}
   346  
   347  	delete(p.ipv4Allocation, allocation)
   348  	p.config.AvailableIpv4Addresses++
   349  	p.config.AllocatedIpv4Addresses--
   350  	p.ipv4Pool = append(p.ipv4Pool, ip)
   351  
   352  	return nil
   353  }
   354  
   355  func (p *IpPool) AllocateIpv6(allocation string) (string, error) {
   356  	if ip, ok := p.ipv6Allocation[allocation]; ok {
   357  		return ip, nil
   358  	}
   359  
   360  	l := len(p.ipv6Pool)
   361  	if l == 0 {
   362  		return "", errNoIpAvailable
   363  	}
   364  
   365  	ip := p.ipv6Pool[l-1]
   366  
   367  	p.config.AvailableIpv6Addresses--
   368  	p.config.AllocatedIpv6Addresses++
   369  	p.ipv6Pool = p.ipv6Pool[:l-1]
   370  	p.ipv6Allocation[allocation] = ip
   371  
   372  	return ip, nil
   373  }
   374  
   375  func (p *IpPool) ReleaseIpv6(allocation string) error {
   376  	ip, ok := p.ipv6Allocation[allocation]
   377  	if !ok {
   378  		return errInvalidAllocation
   379  	}
   380  
   381  	delete(p.ipv6Allocation, allocation)
   382  	p.config.AvailableIpv6Addresses++
   383  	p.config.AllocatedIpv6Addresses--
   384  	p.ipv6Pool = append(p.ipv6Pool, ip)
   385  
   386  	return nil
   387  }