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 }