* Petr Pudlák <[email protected]> [2014-07-04 16:13:06 +0200]: > Do I understand it correctly that current `ext_reservations` will be kept > outside address pools, just at the network object? And only the IPs used by > instances will be inside 'pools'? >
Exactly. This is supposed to reduce the use of bitarrays. Well the design doc is still under review but I guess it won't change dramatically. Anyway if you think that this patch will not complicate things in Haskell later then fine by me. Of course if you come up with something that will make future development easier wrt networks refactoring, it would be highly appreciated :) Cheers, dimara > Thanks, > Petr > > > On Fri, Jul 4, 2014 at 3:53 PM, Dimitris Aragiorgis <[email protected]> wrote: > > > * Petr Pudlák <[email protected]> [2014-07-04 15:04:22 +0200]: > > > > > Hi Dimitris, > > > > > > yes, you're right, I didn't realize this patch could make the > > > implementation of the design harder. > > > > > > Actually, before this patch series, networks always had a reservation > > pool. > > > If an AddressPool objects was created for a network, it filled the > > > reservation fields if they were missing. > > > > > > One thing that we could do is to keep the AddressPool abstraction with > > > instance and external reservations, and instead of having the two fields > > in > > > the Network object, replace them with this AddressPool object. This would > > > require more changes in the Python code, but it'd be better for the > > future > > > refactoring. What do you think? > > > > > > > Well, what I have is mind is something like the following Network object: > > > > net1 { > > subnets [ > > uuid1 { > > name: subnet1 > > cidr: 192.0.2.0/24 > > pools: [ > > {range: {start: 192.0.2.10, end:192.0.2.15, size: 5}, > > reservations: 00000, name:pool1} > > ] > > reserved: [192.0.2.15] > > } > > ] > > } > > > > but this is something (as you said) to be implemented in the future. > > > > All I want to say is that the pool should be a separate entity and not > > merged > > inside the network object. With the desing doc in mind, don't you agree on > > this? > > > > dimara > > > > > Petr > > > > > > > > > On Fri, Jul 4, 2014 at 2:42 PM, Dimitris Aragiorgis <[email protected]> > > wrote: > > > > > > > Hi, > > > > > > > > From the commit message I understand that networks should always have > > > > the reservations bitarrays. Is that correct? Have you had the chance to > > > > see this thread: > > > > > > > > > > https://groups.google.com/forum/#!msg/ganeti-devel/PFSQ-jS3ZSo/6QcG57JY_1cJ > > > > > > > > In a nutshell we want to abstract pools from networks and make them > > > > optional, so that we can decouple the L2 from L3 information and have > > L2 > > > > only networks. I know that implementing such a thing will require a lot > > > > of changes in the Haskell part (since wconfd came into play). > > > > > > > > I am just mentioning this now in case something here (in this patch > > > > series) breaks the design really bad. > > > > > > > > Thanks, > > > > dimara > > > > > > > > PS: Haven't seen the patch yet. Just commenting on the commit message. > > > > > > > > > > > > * 'Petr Pudlak' via ganeti-devel <[email protected]> > > > > [2014-07-04 13:18:03 +0200]: > > > > > > > > > .. in Haskell and work with BitArrays directly. > > > > > > > > > > Having a separate data type AdderssPool doesn't help, as it should be > > > > > always present; if a network doesn't have the table of reservations > > we > > > > > want to add the table anyway. > > > > > (Unlike in Python, where AddressPool modifies its corresponding > > Network > > > > > object.) > > > > > > > > > > Add functions for making reservations on a Network object. > > > > > > > > > > Signed-off-by: Petr Pudlak <[email protected]> > > > > > --- > > > > > src/Ganeti/Constants.hs | 13 +++ > > > > > src/Ganeti/Network.hs | 220 > > > > ++++++++++++++++++++++++++++++----------- > > > > > src/Ganeti/Objects.hs | 8 +- > > > > > src/Ganeti/Query/Network.hs | 8 +- > > > > > test/hs/Test/Ganeti/Network.hs | 100 +++++-------------- > > > > > test/hs/Test/Ganeti/Objects.hs | 12 +-- > > > > > 6 files changed, 215 insertions(+), 146 deletions(-) > > > > > > > > > > diff --git a/src/Ganeti/Constants.hs b/src/Ganeti/Constants.hs > > > > > index 832c86c..7761f7d 100644 > > > > > --- a/src/Ganeti/Constants.hs > > > > > +++ b/src/Ganeti/Constants.hs > > > > > @@ -5009,3 +5009,16 @@ helperVmShutdown = 2 * 60 * 60 > > > > > -- speed of 1/5 of the max speed of current drives. > > > > > zeroingTimeoutPerMib :: Double > > > > > zeroingTimeoutPerMib = 1.0 / (100.0 / 5.0) > > > > > + > > > > > +-- * Networking > > > > > + > > > > > +-- The minimum size of a network. > > > > > +ipv4NetworkMinSize :: Int > > > > > +ipv4NetworkMinSize = 30 > > > > > + > > > > > +-- The maximum size of a network. > > > > > +-- > > > > > +-- FIXME: This limit is for performance reasons. Remove when > > refactoring > > > > > +-- for performance tuning was successful. > > > > > +ipv4NetworkMaxSize :: Int > > > > > +ipv4NetworkMaxSize = 30 > > > > > diff --git a/src/Ganeti/Network.hs b/src/Ganeti/Network.hs > > > > > index 5106213..832cba0 100644 > > > > > --- a/src/Ganeti/Network.hs > > > > > +++ b/src/Ganeti/Network.hs > > > > > @@ -1,3 +1,5 @@ > > > > > +{-# LANGUAGE RankNTypes #-} > > > > > + > > > > > {-| Implementation of the Ganeti network objects. > > > > > > > > > > This is does not (yet) cover all methods that are provided in the > > > > > @@ -27,79 +29,183 @@ Foundation, Inc., 51 Franklin Street, Fifth > > Floor, > > > > Boston, MA > > > > > -} > > > > > > > > > > module Ganeti.Network > > > > > - ( AddressPool(..) > > > > > - , createAddressPool > > > > > - , bitStringToBitVector > > > > > - , allReservations > > > > > + ( PoolPart(..) > > > > > + , netIpv4NumHosts > > > > > , getReservedCount > > > > > , getFreeCount > > > > > , isFull > > > > > , getMap > > > > > - , networkIsValid > > > > > + , isReserved > > > > > + , reserve > > > > > + , release > > > > > + , findFree > > > > > + , allReservations > > > > > + , reservations > > > > > + , extReservations > > > > > ) where > > > > > > > > > > -import qualified Data.Vector.Unboxed as V > > > > > +import Control.Monad > > > > > +import Control.Monad.Error > > > > > +import Control.Monad.State > > > > > +import Data.Function (on) > > > > > > > > > > +import Ganeti.BasicTypes > > > > > +import qualified Ganeti.Constants as C > > > > > +import Ganeti.Lens > > > > > import Ganeti.Objects > > > > > +import Ganeti.Objects.Lens > > > > > +import qualified Ganeti.Objects.BitArray as BA > > > > > > > > > > --- | An address pool, holding a network plus internal and external > > > > > --- reservations. > > > > > -data AddressPool = AddressPool { network :: Network, > > > > > - reservations :: V.Vector Bool, > > > > > - extReservations :: V.Vector Bool } > > > > > - deriving (Show) > > > > > - > > > > > --- | Create an address pool from a network. > > > > > -createAddressPool :: Network -> Maybe AddressPool > > > > > -createAddressPool n > > > > > - | networkIsValid n = > > > > > - let res = maybeStr2BitVec $ networkReservations n > > > > > - ext_res = maybeStr2BitVec $ networkExtReservations n > > > > > - in Just AddressPool { reservations = res > > > > > - , extReservations = ext_res > > > > > - , network = n } > > > > > - | otherwise = Nothing > > > > > - > > > > > --- | Checks the consistency of the network object. So far, only > > checks > > > > the > > > > > --- length of the reservation strings. > > > > > -networkIsValid :: Network -> Bool > > > > > -networkIsValid n = > > > > > - sameLength (networkReservations n) (networkExtReservations n) > > > > > - > > > > > --- | Checks if two maybe strings are both nothing or of equal > > length. > > > > > -sameLength :: Maybe String -> Maybe String -> Bool > > > > > -sameLength Nothing Nothing = True > > > > > -sameLength (Just s1) (Just s2) = length s1 == length s2 > > > > > -sameLength _ _ = False > > > > > - > > > > > --- | Converts a maybe bit string to a bit vector. Returns an empty > > bit > > > > vector on > > > > > --- nothing. > > > > > -maybeStr2BitVec :: Maybe String -> V.Vector Bool > > > > > -maybeStr2BitVec (Just s) = bitStringToBitVector s > > > > > -maybeStr2BitVec Nothing = V.fromList ([]::[Bool]) > > > > > - > > > > > --- | Converts a string to a bit vector. The character '0' is > > interpreted > > > > > --- as 'False', all others as 'True'. > > > > > -bitStringToBitVector :: String -> V.Vector Bool > > > > > -bitStringToBitVector = V.fromList . map (/= '0') > > > > > +ipv4NumHosts :: (Integral n) => n -> Integer > > > > > +ipv4NumHosts mask = 2^(32 - mask) > > > > > + > > > > > +ipv4NetworkMinNumHosts :: Integer > > > > > +ipv4NetworkMinNumHosts = ipv4NumHosts C.ipv4NetworkMinSize > > > > > + > > > > > +ipv4NetworkMaxNumHosts :: Integer > > > > > +ipv4NetworkMaxNumHosts = ipv4NumHosts C.ipv4NetworkMaxSize > > > > > + > > > > > +data PoolPart = PoolInstances | PoolExt > > > > > + > > > > > +poolLens :: PoolPart -> Lens' Network (Maybe BA.BitArray) > > > > > +poolLens PoolInstances = networkReservationsL > > > > > +poolLens PoolExt = networkExtReservationsL > > > > > + > > > > > +netIpv4NumHosts :: Network -> Integer > > > > > +netIpv4NumHosts = ipv4NumHosts . ip4netMask . networkNetwork > > > > > + > > > > > +-- | Creates a new bit array pool of the appropriate size > > > > > +newPool :: (MonadError e m, Error e) => Network -> m BA.BitArray > > > > > +newPool net = do > > > > > + let numhosts = netIpv4NumHosts net > > > > > + when (numhosts > ipv4NetworkMaxNumHosts) . failError $ > > > > > + "A big network with " ++ show numhosts ++ " host(s) is > > currently" > > > > > + ++ " not supported, please specify at most a /" > > > > > + ++ show ipv4NetworkMaxNumHosts ++ " network" > > > > > + when (numhosts < ipv4NetworkMinNumHosts) . failError $ > > > > > + "A network with only " ++ show numhosts ++ " host(s) is too > > small," > > > > > + ++ " please specify at least a /" > > > > > + ++ show ipv4NetworkMinNumHosts ++ " network" > > > > > + return $ BA.zeroes (fromInteger numhosts) > > > > > + > > > > > +-- | A helper function that creates a bit array pool, of it's > > missing. > > > > > +orNewPool :: (MonadError e m, Error e) > > > > > + => Network -> Maybe BA.BitArray -> m BA.BitArray > > > > > +orNewPool net = maybe (newPool net) return > > > > > + > > > > > +withPool :: (MonadError e m, Error e) > > > > > + => PoolPart -> (Network -> BA.BitArray -> m (a, > > BA.BitArray)) > > > > > + -> StateT Network m a > > > > > +withPool part f = StateT $ \n -> mapMOf2 (poolLens part) (f' n) n > > > > > + where > > > > > + f' net = liftM (over _2 Just) . f net <=< orNewPool net > > > > > + > > > > > +withPool_ :: (MonadError e m, Error e) > > > > > + => PoolPart -> (Network -> BA.BitArray -> m BA.BitArray) > > > > > + -> Network -> m Network > > > > > +withPool_ part f = execStateT $ withPool part ((liftM ((,) ()) .) . > > f) > > > > > + > > > > > +readPool :: PoolPart -> Network -> Maybe BA.BitArray > > > > > +readPool = view . poolLens > > > > > + > > > > > +readPoolE :: (MonadError e m, Error e) > > > > > + => PoolPart -> Network -> m BA.BitArray > > > > > +readPoolE part net = orNewPool net (readPool part net) > > > > > + > > > > > +readAllE :: (MonadError e m, Error e) > > > > > + => Network -> m BA.BitArray > > > > > +readAllE net = do > > > > > + res <- orNewPool net $ networkReservations net > > > > > + ext <- orNewPool net $ networkExtReservations net > > > > > + return $ res BA.-|- ext > > > > > + > > > > > +reservations :: Network -> Maybe BA.BitArray > > > > > +reservations = readPool PoolInstances > > > > > + > > > > > +extReservations :: Network -> Maybe BA.BitArray > > > > > +extReservations = readPool PoolExt > > > > > > > > > > -- | Get a bit vector of all reservations (internal and external) > > > > combined. > > > > > -allReservations :: AddressPool -> V.Vector Bool > > > > > -allReservations a = V.zipWith (||) (reservations a) > > (extReservations a) > > > > > +allReservations :: Network -> Maybe BA.BitArray > > > > > +allReservations a = (BA.-|-) `liftM` reservations a `ap` > > > > extReservations a > > > > > > > > > > -- | Get the count of reserved addresses. > > > > > -getReservedCount :: AddressPool -> Int > > > > > -getReservedCount = V.length . V.filter (== True) . allReservations > > > > > +getReservedCount :: Network -> Int > > > > > +getReservedCount = maybe 0 BA.count1 . allReservations > > > > > > > > > > -- | Get the count of free addresses. > > > > > -getFreeCount :: AddressPool -> Int > > > > > -getFreeCount = V.length . V.filter (== False) . allReservations > > > > > +getFreeCount :: Network -> Int > > > > > +getFreeCount = maybe 0 BA.count0 . allReservations > > > > > > > > > > -- | Check whether the network is full. > > > > > -isFull :: AddressPool -> Bool > > > > > -isFull = V.and . allReservations > > > > > +isFull :: Network -> Bool > > > > > +isFull = (0 ==) . getFreeCount > > > > > > > > > > -- | Return a textual representation of the network's occupation > > status. > > > > > -getMap :: AddressPool -> String > > > > > -getMap = V.toList . V.map mapPixel . allReservations > > > > > - where mapPixel c = if c then 'X' else '.' > > > > > +getMap :: Network -> String > > > > > +getMap = maybe "" (BA.asString '.' 'X') . allReservations > > > > > + > > > > > +-- * Functions used for manipulating the reservations > > > > > + > > > > > +-- | Returns an address index wrt a network. > > > > > +-- Fails if the address isn't in the network range. > > > > > +addrIndex :: (MonadError e m, Error e) => Ip4Address -> Network -> > > m Int > > > > > +addrIndex addr net = do > > > > > + let n = networkNetwork net > > > > > + i = on (-) ip4AddressToNumber addr (ip4netAddr n) > > > > > + when ((i < 0) || (i >= ipv4NumHosts (ip4netMask n))) . failError > > > > > + $ "Address '" ++ show addr ++ "' not in the network '" ++ show > > net > > > > ++ "'" > > > > > + return $ fromInteger i > > > > > + > > > > > +-- | Returns an address of a given index wrt a network. > > > > > +-- Fails if the index isn't in the network range. > > > > > +addrAt :: (MonadError e m, Error e) => Int -> Network -> m > > Ip4Address > > > > > +addrAt i net | (i' < 0) || (i' >= ipv4NumHosts (ip4netMask n)) = > > > > > + failError $ "Requested index " ++ show i > > > > > + ++ " outside the range of network '" ++ show net ++ > > "'" > > > > > + | otherwise = > > > > > + return $ ip4AddressFromNumber (ip4AddressToNumber (ip4netAddr > > n) + > > > > i') > > > > > + where > > > > > + n = networkNetwork net > > > > > + i' = toInteger i > > > > > + > > > > > +-- | Checks if a given address is reserved. > > > > > +-- Fails if the address isn't in the network range. > > > > > +isReserved :: (MonadError e m, Error e) => > > > > > + PoolPart -> Ip4Address -> Network -> m Bool > > > > > +isReserved part addr net = > > > > > + (BA.!) `liftM` readPoolE part net `ap` addrIndex addr net > > > > > + > > > > > +-- | Marks an address as used. > > > > > +reserve :: (MonadError e m, Error e) => > > > > > + PoolPart -> Ip4Address -> Network -> m Network > > > > > +reserve part addr = > > > > > + withPool_ part $ \net ba -> do > > > > > + idx <- addrIndex addr net > > > > > + let addrs = show addr > > > > > + when (ba BA.! idx) . failError $ case part of > > > > > + PoolExt -> "IP " ++ addrs ++ " is already externally > > reserved" > > > > > + PoolInstances -> "IP " ++ addrs ++ " is already used by an > > > > instance" > > > > > + BA.setAt idx True ba > > > > > + > > > > > +-- | Marks an address as unused. > > > > > +release :: (MonadError e m, Error e) => > > > > > + PoolPart -> Ip4Address -> Network -> m Network > > > > > +release part addr = > > > > > + withPool_ part $ \net ba -> do > > > > > + idx <- addrIndex addr net > > > > > + let addrs = show addr > > > > > + unless (ba BA.! idx) . failError $ case part of > > > > > + PoolExt -> "IP " ++ addrs ++ " is not externally reserved" > > > > > + PoolInstances -> "IP " ++ addrs ++ " is not used by an > > instance" > > > > > + BA.setAt idx False ba > > > > > + > > > > > +-- | Get the first free address in the network > > > > > +-- that satisfies a given predicate. > > > > > +findFree :: (MonadError e m, Error e) > > > > > + => (Ip4Address -> Bool) -> Network -> m (Maybe Ip4Address) > > > > > +findFree p net = readAllE net >>= BA.foldr f (return Nothing) > > > > > + where > > > > > + addrAtEither = addrAt :: Int -> Network -> Either String > > Ip4Address > > > > > + f False i _ | Right a <- addrAtEither i net, p a = return (Just > > a) > > > > > + f _ _ x = x > > > > > diff --git a/src/Ganeti/Objects.hs b/src/Ganeti/Objects.hs > > > > > index 730a39b..1817537 100644 > > > > > --- a/src/Ganeti/Objects.hs > > > > > +++ b/src/Ganeti/Objects.hs > > > > > @@ -120,6 +120,7 @@ import qualified AutoConf > > > > > import qualified Ganeti.Constants as C > > > > > import qualified Ganeti.ConstantUtils as ConstantUtils > > > > > import Ganeti.JSON > > > > > +import qualified Ganeti.Objects.BitArray as BA > > > > > import Ganeti.Types > > > > > import Ganeti.THH > > > > > import Ganeti.THH.Field > > > > > @@ -241,6 +242,9 @@ instance JSON Ip4Network where > > > > > > > > > > -- FIXME: Not all types might be correct here, since they > > > > > -- haven't been exhaustively deduced from the python code yet. > > > > > +-- > > > > > +-- FIXME: When parsing, check that the ext_reservations and > > reservations > > > > > +-- have the same length > > > > > $(buildObject "Network" "network" $ > > > > > [ simpleField "name" [t| NonEmptyString |] > > > > > , optionalField $ > > > > > @@ -253,9 +257,9 @@ $(buildObject "Network" "network" $ > > > > > , optionalField $ > > > > > simpleField "gateway6" [t| String |] > > > > > , optionalField $ > > > > > - simpleField "reservations" [t| String |] > > > > > + simpleField "reservations" [t| BA.BitArray |] > > > > > , optionalField $ > > > > > - simpleField "ext_reservations" [t| String |] > > > > > + simpleField "ext_reservations" [t| BA.BitArray |] > > > > > ] > > > > > ++ uuidFields > > > > > ++ timeStampFields > > > > > diff --git a/src/Ganeti/Query/Network.hs > > b/src/Ganeti/Query/Network.hs > > > > > index 3a4a31b..19bf140 100644 > > > > > --- a/src/Ganeti/Query/Network.hs > > > > > +++ b/src/Ganeti/Query/Network.hs > > > > > @@ -61,14 +61,14 @@ networkFields = > > > > > FieldSimple (rsMaybeUnavail . networkMacPrefix), QffNormal) > > > > > , (FieldDefinition "free_count" "FreeCount" QFTNumber "Number of > > > > available\ > > > > > \ addresses", > > > > > - FieldSimple (rsMaybeNoData . fmap getFreeCount . > > > > createAddressPool), > > > > > + FieldSimple (rsNormal . getFreeCount), > > > > > QffNormal) > > > > > , (FieldDefinition "map" "Map" QFTText "Actual mapping", > > > > > - FieldSimple (rsMaybeNoData . fmap getMap . createAddressPool), > > > > > + FieldSimple (rsNormal . getMap), > > > > > QffNormal) > > > > > , (FieldDefinition "reserved_count" "ReservedCount" QFTNumber > > > > > "Number of reserved addresses", > > > > > - FieldSimple (rsMaybeNoData . fmap getReservedCount . > > > > createAddressPool), > > > > > + FieldSimple (rsNormal . getReservedCount), > > > > > QffNormal) > > > > > , (FieldDefinition "group_list" "GroupList" QFTOther > > > > > "List of nodegroups (group name, NIC mode, NIC link)", > > > > > @@ -175,5 +175,5 @@ getReservations net = > > > > > getExtReservationsString :: Network -> ResultEntry > > > > > getExtReservationsString net = > > > > > let addrs = getReservations (networkNetwork net) > > > > > - (fromMaybe "" $ networkExtReservations net) > > > > > + (maybe "" show $ networkExtReservations net) > > > > > in rsNormal . intercalate ", " $ map show addrs > > > > > diff --git a/test/hs/Test/Ganeti/Network.hs > > > > b/test/hs/Test/Ganeti/Network.hs > > > > > index 57793ca..58fee6f 100644 > > > > > --- a/test/hs/Test/Ganeti/Network.hs > > > > > +++ b/test/hs/Test/Ganeti/Network.hs > > > > > @@ -6,72 +6,22 @@ module Test.Ganeti.Network > > > > > , genBitStringMaxLen > > > > > ) where > > > > > > > > > > +import Data.Maybe (fromMaybe) > > > > > + > > > > > import Test.QuickCheck > > > > > > > > > > import Ganeti.Network as Network > > > > > import Ganeti.Objects as Objects > > > > > +import Ganeti.Objects.BitArray as BA > > > > > > > > > > -import Test.Ganeti.Objects > > > > > - ( genBitStringMaxLen > > > > > - , genValidNetwork ) > > > > > -import Test.Ganeti.TestHelper > > > > > +import Test.Ganeti.Objects ( genBitStringMaxLen ) > > > > > import Test.Ganeti.TestCommon > > > > > - > > > > > -import qualified Data.Vector.Unboxed as V > > > > > - > > > > > --- * Generators and arbitrary instances > > > > > - > > > > > --- | Generates address pools. The size of the network is > > intentionally > > > > > --- decoupled from the size of the bit vectors, to avoid slowing down > > > > > --- the tests by generating unnecessary bit strings. > > > > > -genAddressPool :: Int -> Gen AddressPool > > > > > -genAddressPool maxLenBitVec = do > > > > > - -- Generating networks with netmask of minimum /24 to avoid too > > long > > > > > - -- bit strings being generated. > > > > > - net <- genValidNetwork > > > > > - lenBitVec <- choose (0, maxLenBitVec) > > > > > - res <- genBitVector lenBitVec > > > > > - ext_res <- genBitVector lenBitVec > > > > > - return AddressPool { network = net > > > > > - , reservations = res > > > > > - , extReservations = ext_res } > > > > > - > > > > > --- | Generates an arbitrary bit vector of the given length. > > > > > -genBitVector :: Int -> Gen (V.Vector Bool) > > > > > -genBitVector len = do > > > > > - boolList <- vector len::Gen [Bool] > > > > > - return $ V.fromList boolList > > > > > - > > > > > -instance Arbitrary AddressPool where > > > > > - arbitrary = genAddressPool ((2::Int)^(8::Int)) > > > > > +import Test.Ganeti.TestHelper > > > > > > > > > > -- * Test cases > > > > > > > > > > --- | Check the mapping of bit strings to bit vectors > > > > > -prop_bitStringToBitVector :: Property > > > > > -prop_bitStringToBitVector = > > > > > - forAll (genBitStringMaxLen 256) $ \bs -> > > > > > - let bitList = V.toList $ Network.bitStringToBitVector bs > > > > > - bitCharList = Prelude.zip bitList bs > > > > > - in Prelude.all checkBit bitCharList > > > > > - > > > > > --- | Check whether an element of a bit vector is consistent with an > > > > element > > > > > --- of a bit string (containing '0' and '1' characters). > > > > > -checkBit :: (Bool, Char) -> Bool > > > > > -checkBit (False, '0') = True > > > > > -checkBit (True, '1') = True > > > > > -checkBit _ = False > > > > > - > > > > > --- | Check creation of an address pool when a network is given. > > > > > -prop_createAddressPool :: Objects.Network -> Property > > > > > -prop_createAddressPool n = > > > > > - let valid = networkIsValid n > > > > > - in case createAddressPool n of > > > > > - Just _ -> True ==? valid > > > > > - Nothing -> False ==? valid > > > > > - > > > > > -- | Check that the address pool's properties are calculated > > correctly. > > > > > -prop_addressPoolProperties :: AddressPool -> Property > > > > > +prop_addressPoolProperties :: Network -> Property > > > > > prop_addressPoolProperties a = > > > > > conjoin > > > > > [ printTestCase > > > > > @@ -93,40 +43,38 @@ prop_addressPoolProperties a = > > > > > show a) (checkGetMap a) > > > > > ] > > > > > > > > > > +-- | Checks for the subset relation on 'Maybe' values. > > > > > +subsetMaybe :: Maybe BitArray -> Maybe BitArray -> Bool > > > > > +subsetMaybe (Just x) (Just y) = subset x y > > > > > +subsetMaybe x y = x == y -- only if they're both Nothing > > > > > + > > > > > -- | Check that all internally reserved ips are included in > > > > 'allReservations'. > > > > > -allReservationsSubsumesInternal :: AddressPool -> Bool > > > > > +allReservationsSubsumesInternal :: Network -> Bool > > > > > allReservationsSubsumesInternal a = > > > > > - bitVectorSubsumes (allReservations a) (reservations a) > > > > > + reservations a `subsetMaybe` allReservations a > > > > > > > > > > -- | Check that all externally reserved ips are included in > > > > 'allReservations'. > > > > > -allReservationsSubsumesExternal :: AddressPool -> Bool > > > > > +allReservationsSubsumesExternal :: Network -> Bool > > > > > allReservationsSubsumesExternal a = > > > > > - bitVectorSubsumes (allReservations a) (extReservations a) > > > > > - > > > > > --- | Checks if one bit vector subsumes the other one. > > > > > -bitVectorSubsumes :: V.Vector Bool -> V.Vector Bool -> Bool > > > > > -bitVectorSubsumes v1 v2 = V.and $ > > > > > - V.zipWith (\a b -> not b || a) v1 v2 > > > > > + extReservations a `subsetMaybe` allReservations a > > > > > > > > > > -- | Check that the counts of free and reserved ips add up. > > > > > -checkCounts :: AddressPool -> Bool > > > > > +checkCounts :: Network -> Property > > > > > checkCounts a = > > > > > - let res = reservations a > > > > > - in V.length res == getFreeCount a + getReservedCount a > > > > > + netIpv4NumHosts a ==? toInteger (getFreeCount a + > > getReservedCount a) > > > > > > > > > > -- | Check that the detection of a full network works correctly. > > > > > -checkIsFull :: AddressPool -> Bool > > > > > -checkIsFull a = isFull a == V.notElem False (allReservations a) > > > > > +checkIsFull :: Network -> Property > > > > > +checkIsFull a = > > > > > + isFull a ==? maybe True (and . toList) (allReservations a) > > > > > > > > > > -- | Check that the map representation of the network corresponds > > to the > > > > > -- network's reservations. > > > > > -checkGetMap :: AddressPool -> Bool > > > > > +checkGetMap :: Network -> Property > > > > > checkGetMap a = > > > > > - allReservations a == V.fromList (Prelude.map (== 'X') (getMap a)) > > > > > + fromMaybe BA.empty (allReservations a) > > > > > + ==? fromList (Prelude.map (== 'X') (getMap a)) > > > > > > > > > > testSuite "Network" > > > > > - [ 'prop_bitStringToBitVector > > > > > - , 'prop_createAddressPool > > > > > - , 'prop_addressPoolProperties > > > > > + [ 'prop_addressPoolProperties > > > > > ] > > > > > - > > > > > diff --git a/test/hs/Test/Ganeti/Objects.hs > > > > b/test/hs/Test/Ganeti/Objects.hs > > > > > index 8377a31..f3c224f 100644 > > > > > --- a/test/hs/Test/Ganeti/Objects.hs > > > > > +++ b/test/hs/Test/Ganeti/Objects.hs > > > > > @@ -61,6 +61,7 @@ import Test.Ganeti.Types () > > > > > import qualified Ganeti.Constants as C > > > > > import Ganeti.Network > > > > > import Ganeti.Objects as Objects > > > > > +import qualified Ganeti.Objects.BitArray as BA > > > > > import Ganeti.JSON > > > > > import Ganeti.Types > > > > > > > > > > @@ -283,12 +284,12 @@ genValidNetwork = do > > > > > return n > > > > > > > > > > -- | Generate an arbitrary string consisting of '0' and '1' of the > > > > given length. > > > > > -genBitString :: Int -> Gen String > > > > > -genBitString len = vectorOf len (elements "01") > > > > > +genBitString :: Int -> Gen BA.BitArray > > > > > +genBitString len = BA.fromList `liftM` vectorOf len (elements > > [False, > > > > True]) > > > > > > > > > > -- | Generate an arbitrary string consisting of '0' and '1' of the > > > > maximum given > > > > > -- length. > > > > > -genBitStringMaxLen :: Int -> Gen String > > > > > +genBitStringMaxLen :: Int -> Gen BA.BitArray > > > > > genBitStringMaxLen maxLen = choose (0, maxLen) >>= genBitString > > > > > > > > > > -- | Generator for config data with an empty cluster (no instances), > > > > > @@ -448,10 +449,7 @@ casePyCompatNetworks = do > > > > > -- to be compared against the same properties generated by the > > python > > > > code. > > > > > getNetworkProperties :: Network -> (Int, Int, Network) > > > > > getNetworkProperties net = > > > > > - let maybePool = createAddressPool net > > > > > - in case maybePool of > > > > > - (Just pool) -> (getFreeCount pool, getReservedCount pool, > > > > net) > > > > > - Nothing -> (-1, -1, net) > > > > > + (getFreeCount net, getReservedCount net, net) > > > > > > > > > > -- | Tests the compatibility between Haskell-serialized node groups > > and > > > > their > > > > > -- python-decoded and encoded version. > > > > > -- > > > > > 2.0.0.526.g5318336 > > > > > >
signature.asc
Description: Digital signature
