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

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