func (*AddressSuite) TestIPv4ToDecimal(c *gc.C) { zeroIP, err := network.IPv4ToDecimal(net.ParseIP("0.0.0.0")) c.Assert(err, jc.ErrorIsNil) c.Assert(zeroIP, gc.Equals, uint32(0)) nilIP := net.ParseIP("bad format") _, err = network.IPv4ToDecimal(nilIP) c.Assert(err, gc.ErrorMatches, `"<nil>" is not a valid IPv4 address`) _, err = network.IPv4ToDecimal(net.ParseIP("2001:db8::1")) c.Assert(err, gc.ErrorMatches, `"2001:db8::1" is not a valid IPv4 address`) nonZeroIP, err := network.IPv4ToDecimal(net.ParseIP("192.168.1.1")) c.Assert(err, jc.ErrorIsNil) c.Assert(nonZeroIP, gc.Equals, uint32(3232235777)) }
// attemptToPickNewAddress will try to pick a new address. It can fail // with AlreadyExists due to a race condition between fetching the // list of addresses already in use and allocating a new one. If the // subnet is not alive, it will also fail. It is called in a loop by // PickNewAddress until it gets one or there are no more available! func (s *Subnet) attemptToPickNewAddress() (*IPAddress, error) { if s.doc.Life != Alive { return nil, errors.Errorf("cannot pick address: subnet %q is not alive", s) } high := s.doc.AllocatableIPHigh low := s.doc.AllocatableIPLow if low == "" || high == "" { return nil, errors.Errorf("no allocatable IP addresses for subnet %q", s) } // convert low and high to decimals as the bounds lowDecimal, err := network.IPv4ToDecimal(net.ParseIP(low)) if err != nil { // these addresses are validated so should never happen return nil, errors.Annotatef(err, "invalid AllocatableIPLow %q for subnet %q", low, s) } highDecimal, err := network.IPv4ToDecimal(net.ParseIP(high)) if err != nil { // these addresses are validated so should never happen return nil, errors.Annotatef(err, "invalid AllocatableIPHigh %q for subnet %q", high, s) } // find all addresses for this subnet and convert them to decimals addresses, closer := s.st.getCollection(ipaddressesC) defer closer() id := s.ID() var doc struct { Value string } allocated := make(map[uint32]bool) iter := addresses.Find(bson.D{{"subnetid", id}}).Iter() for iter.Next(&doc) { // skip invalid values. Can't happen anyway as we validate. value, err := network.IPv4ToDecimal(net.ParseIP(doc.Value)) if err != nil { continue } allocated[value] = true } if err := iter.Close(); err != nil { return nil, errors.Annotatef(err, "cannot read addresses of subnet %q", s) } // Check that the number of addresses in use is less than the // difference between low and high - i.e. we haven't exhausted all // possible addresses. if len(allocated) >= int(highDecimal-lowDecimal)+1 { return nil, errors.Errorf("allocatable IP addresses exhausted for subnet %q", s) } // pick a new random decimal between the low and high bounds that // doesn't match an existing one newDecimal := pickAddress(lowDecimal, highDecimal, allocated) // convert it back to a dotted-quad newIP := network.DecimalToIPv4(newDecimal) newAddr := network.NewAddress(newIP.String()) // and create a new IPAddress from it and return it return s.st.AddIPAddress(newAddr, s.ID()) }