summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cobbler/IPy.py1267
-rw-r--r--cobbler/api.py49
-rw-r--r--cobbler/check.py47
-rw-r--r--cobbler/collection.py25
-rw-r--r--cobbler/distros.py1
-rw-r--r--cobbler/item.py8
-rw-r--r--cobbler/profile.py3
-rw-r--r--cobbler/profiles.py1
-rw-r--r--cobbler/serializable.py12
-rw-r--r--cobbler/serializer.py19
-rw-r--r--cobbler/serializer_tools.py218
-rw-r--r--cobbler/settings.py34
-rw-r--r--cobbler/sync.py66
-rw-r--r--cobbler/systems.py1
-rw-r--r--cobbler/util.py8
-rw-r--r--cobbler/~api.py0
-rw-r--r--tests/tests.py72
17 files changed, 198 insertions, 1633 deletions
diff --git a/cobbler/IPy.py b/cobbler/IPy.py
deleted file mode 100644
index f0d2bcb..0000000
--- a/cobbler/IPy.py
+++ /dev/null
@@ -1,1267 +0,0 @@
-""" IPy - class and tools for handling of IPv4 and IPv6 Addresses and Networks.
-
-$HeadURL: http://svn.23.nu/svn/repos/IPy/trunk/IPy.py $
-
-$Id: IPy.py 671 2004-08-22 21:02:29Z md $
-
-The IP class allows a comfortable parsing and handling for most
-notations in use for IPv4 and IPv6 Addresses and Networks. It was
-greatly inspired bei RIPE's Perl module NET::IP's interface but
-doesn't share the Implementation. It doesn't share non-CIDR netmasks,
-so funky stuff lixe a netmask 0xffffff0f can't be done here.
-
- >>> ip = IP('127.0.0.0/30')
- >>> for x in ip:
- ... print x
- ...
- 127.0.0.0
- 127.0.0.1
- 127.0.0.2
- 127.0.0.3
- >>> ip2 = IP('0x7f000000/30')
- >>> ip == ip2
- 1
- >>> ip.reverseNames()
- ['0.0.0.127.in-addr.arpa.', '1.0.0.127.in-addr.arpa.', '2.0.0.127.in-addr.arpa.', '3.0.0.127.in-addr.arpa.']
- >>> ip.reverseName()
- '0-3.0.0.127.in-addr.arpa.'
- >>> ip.iptype()
- 'PRIVATE'
-
-It can detect about a dozen different ways of expressing IP addresses
-and networks, parse them and distinguish between IPv4 and IPv6 addresses.
-
- >>> IP('10.0.0.0/8').version()
- 4
- >>> IP('::1').version()
- 6
- >>> print IP(0x7f000001)
- 127.0.0.1
- >>> print IP('0x7f000001')
- 127.0.0.1
- >>> print IP('127.0.0.1')
- 127.0.0.1
- >>> print IP('10')
- 10.0.0.0
- >>> print IP('1080:0:0:0:8:800:200C:417A')
- 1080:0000:0000:0000:0008:0800:200c:417a
- >>> print IP('1080::8:800:200C:417A')
- 1080:0000:0000:0000:0008:0800:200c:417a
- >>> print IP('::1')
- 0000:0000:0000:0000:0000:0000:0000:0001
- >>> print IP('::13.1.68.3')
- 0000:0000:0000:0000:0000:0000:0d01:4403
- >>> print IP('127.0.0.0/8')
- 127.0.0.0/8
- >>> print IP('127.0.0.0/255.0.0.0')
- 127.0.0.0/8
- >>> print IP('127.0.0.0-127.255.255.255')
- 127.0.0.0/8
-
-Nearly all class methods which return a string have an optional
-parameter 'wantprefixlen' which controlles if the prefixlen or netmask
-is printed. Per default the prefilen is always shown if the net
-contains more than one address.
-
-wantprefixlen == 0 / None don't return anything 1.2.3.0
-wantprefixlen == 1 /prefix 1.2.3.0/24
-wantprefixlen == 2 /netmask 1.2.3.0/255.255.255.0
-wantprefixlen == 3 -lastip 1.2.3.0-1.2.3.255
-
-You can also change the defaults on an per-object basis by fiddeling with the class members
-
-NoPrefixForSingleIp
-WantPrefixLen
-
- >>> IP('10.0.0.0/32').strNormal()
- '10.0.0.0'
- >>> IP('10.0.0.0/24').strNormal()
- '10.0.0.0/24'
- >>> IP('10.0.0.0/24').strNormal(0)
- '10.0.0.0'
- >>> IP('10.0.0.0/24').strNormal(1)
- '10.0.0.0/24'
- >>> IP('10.0.0.0/24').strNormal(2)
- '10.0.0.0/255.255.255.0'
- >>> IP('10.0.0.0/24').strNormal(3)
- '10.0.0.0-10.0.0.255'
- >>> ip = IP('10.0.0.0')
- >>> print ip
- 10.0.0.0
- >>> ip.NoPrefixForSingleIp = None
- >>> print ip
- 10.0.0.0/32
- >>> ip.WantPrefixLen = 3
- >>> print ip
- 10.0.0.0-10.0.0.0
-
-
-Further Information might be available at http://c0re.jp/c0de/IPy/
-
-Hacked 2001 by drt@un.bewaff.net
-
-TODO:
- * better comparison (__cmp__ and friends)
- * tests for __cmp__
- * always write hex values lowercase
- * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64
- * move size in bits into class variables to get rid of some "if self._ipversion ..."
- * support for base85 encoding
- * support for output of IPv6 encoded IPv4 Addresses
- * update address type tables
- * first-last notation should be allowed for IPv6
- * add IPv6 docstring examples
- * check better for negative parameters
- * add addition / aggregation
- * move reverse name stuff out of the classes and refactor it
- * support for aggregation of more than two nets at once
- * support for aggregation with "holes"
- * support for finding common prefix
- * '>>' and '<<' for prefix manipulation
- * add our own exceptions instead ValueError all the time
- * rename checkPrefix to checkPrefixOk
- * add more documentation and doctests
- * refactor
-"""
-
-__rcsid__ = '$Id: IPy.py 671 2004-08-22 21:02:29Z md $'
-__version__ = '0.42'
-
-import types
-
-# Definition of the Ranges for IPv4 IPs
-# this should include www.iana.org/assignments/ipv4-address-space
-# and www.iana.org/assignments/multicast-addresses
-IPv4ranges = {
- '0' : 'PUBLIC', # fall back
- '00000000' : 'PRIVATE', # 0/8
- '00001010' : 'PRIVATE', # 10/8
- '01111111' : 'PRIVATE', # 127.0/8
- '1' : 'PUBLIC', # fall back
- '101011000001' : 'PRIVATE', # 172.16/12
- '1100000010101000' : 'PRIVATE', # 192.168/16
- '11011111' : 'RESERVED', # 223/8
- '111' : 'RESERVED' # 224/3
- }
-
-# Definition of the Ranges for IPv6 IPs
-# see also www.iana.org/assignments/ipv6-address-space,
-# www.iana.org/assignments/ipv6-tla-assignments,
-# www.iana.org/assignments/ipv6-multicast-addresses,
-# www.iana.org/assignments/ipv6-anycast-addresses
-IPv6ranges = {
- '00000000' : 'RESERVED', # ::/8
- '00000001' : 'UNASSIGNED', # 100::/8
- '0000001' : 'NSAP', # 200::/7
- '0000010' : 'IPX', # 400::/7
- '0000011' : 'UNASSIGNED', # 600::/7
- '00001' : 'UNASSIGNED', # 800::/5
- '0001' : 'UNASSIGNED', # 1000::/4
- '0010000000000000' : 'RESERVED', # 2000::/16 Reserved
- '0010000000000001' : 'ASSIGNABLE', # 2001::/16 Sub-TLA Assignments [RFC2450]
- '00100000000000010000000': 'ASSIGNABLE IANA', # 2001:0000::/29 - 2001:01F8::/29 IANA
- '00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC
- '00100000000000010000010': 'ASSIGNABLE ARIN', # 2001:0400::/29 - 2001:05F8::/29 ARIN
- '00100000000000010000011': 'ASSIGNABLE RIPE', # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC
- '0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056]
- '0011111111111110' : '6BONE', # 3FFE::/16 6bone Testing [RFC2471]
- '0011111111111111' : 'RESERVED', # 3FFF::/16 Reserved
- '010' : 'GLOBAL-UNICAST', # 4000::/3
- '011' : 'UNASSIGNED', # 6000::/3
- '100' : 'GEO-UNICAST', # 8000::/3
- '101' : 'UNASSIGNED', # A000::/3
- '110' : 'UNASSIGNED', # C000::/3
- '1110' : 'UNASSIGNED', # E000::/4
- '11110' : 'UNASSIGNED', # F000::/5
- '111110' : 'UNASSIGNED', # F800::/6
- '1111110' : 'UNASSIGNED', # FC00::/7
- '111111100' : 'UNASSIGNED', # FE00::/9
- '1111111010' : 'LINKLOCAL', # FE80::/10
- '1111111011' : 'SITELOCAL', # FEC0::/10
- '11111111' : 'MULTICAST', # FF00::/8
- '0' * 96 : 'IPV4COMP', # ::/96
- '0' * 80 + '1' * 16 : 'IPV4MAP', # ::FFFF:0:0/96
- '0' * 128 : 'UNSPECIFIED', # ::/128
- '0' * 127 + '1' : 'LOOPBACK' # ::1/128
- }
-
-
-class IPint:
- """Handling of IP addresses returning integers.
-
- Use class IP instead because some features are not implemented for
- IPint."""
-
- def __init__(self, data, ipversion = 0):
- """Create an instance of an IP object.
-
- Data can be a network specification or a single IP. IP
- Addresses can be specified in all forms understood by
- parseAddress.() the size of a network can be specified as
-
- /prefixlen a.b.c.0/24 2001:658:22a:cafe::/64
- -lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff
- /decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6
-
- If no size specification is given a size of 1 address (/32 for
- IPv4 and /128 for IPv6) is assumed.
-
- >>> print IP('127.0.0.0/8')
- 127.0.0.0/8
- >>> print IP('127.0.0.0/255.0.0.0')
- 127.0.0.0/8
- >>> print IP('127.0.0.0-127.255.255.255')
- 127.0.0.0/8
-
- See module documentation for more examples.
- """
-
- self.NoPrefixForSingleIp = 1 # Print no Prefixlen for /32 and /128
- self.WantPrefixLen = None # Do we want prefix printed by default? see _printPrefix()
-
- netbits = 0
- prefixlen = -1
-
- # handling of non string values in constructor
- if type(data) == types.IntType or type(data) == types.LongType:
- self.ip = long(data)
- if ipversion == 0:
- if self.ip < 0x100000000L:
- ipversion = 4
- else:
- ipversion = 6
- if ipversion == 4:
- prefixlen = 32
- elif ipversion == 6:
- prefixlen = 128
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
- self._ipversion = ipversion
- self._prefixlen = prefixlen
- # handle IP instance as an parameter
- elif isinstance(data, IPint):
- self._ipversion = data._ipversion
- self._prefixlen = data._prefixlen
- self.ip = data.ip
- else:
- # TODO: refactor me!
- # splitting of a string into IP and prefixlen et. al.
- x = data.split('-')
- if len(x) == 2:
- # a.b.c.0-a.b.c.255 specification ?
- (ip, last) = x
- (self.ip, parsedVersion) = parseAddress(ip)
- if parsedVersion != 4:
- raise ValueError, "first-last notation only allowed for IPv4"
- (last, lastversion) = parseAddress(last)
- if lastversion != 4:
- raise ValueError, "last address should be IPv4, too"
- if last < self.ip:
- raise ValueError, "last address should be larger than first"
- size = last - self.ip
- netbits = _count1Bits(size)
- elif len(x) == 1:
- x = data.split('/')
- # if no prefix is given use defaults
- if len(x) == 1:
- ip = x[0]
- prefixlen = -1
- elif len(x) > 2:
- raise ValueError, "only one '/' allowed in IP Address"
- else:
- (ip, prefixlen) = x
- if prefixlen.find('.') != -1:
- # check if the user might have used a netmask like
- # a.b.c.d/255.255.255.0
- (netmask, vers) = parseAddress(prefixlen)
- if vers != 4:
- raise ValueError, "netmask must be IPv4"
- prefixlen = _netmaskToPrefixlen(netmask)
- elif len(x) > 2:
- raise ValueError, "only one '-' allowed in IP Address"
- else:
- raise ValueError, "can't parse"
-
- (self.ip, parsedVersion) = parseAddress(ip)
- if ipversion == 0:
- ipversion = parsedVersion
- if prefixlen == -1:
- if ipversion == 4:
- prefixlen = 32 - netbits
- elif ipversion == 6:
- prefixlen = 128 - netbits
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
- self._ipversion = ipversion
- self._prefixlen = int(prefixlen)
-
- if not _checkNetaddrWorksWithPrefixlen(self.ip, self._prefixlen, self._ipversion):
- raise ValueError, "%s goes not well with prefixlen %d" % (hex(self.ip), self._prefixlen)
-
-
- def int(self):
- """Return the first / base / network addess as an (long) integer.
-
- The same as IP[0].
-
- >>> hex(IP('10.0.0.0/8').int())
- '0xA000000L'
- """
- return self.ip
-
- def version(self):
- """Return the IP version of this Object.
-
- >>> IP('10.0.0.0/8').version()
- 4
- >>> IP('::1').version()
- 6
- """
- return self._ipversion
-
- def prefixlen(self):
- """Returns Network Prefixlen.
-
- >>> IP('10.0.0.0/8').prefixlen()
- 8
- """
- return self._prefixlen
-
- def net(self):
- """Return the base (first) address of a network as an (long) integer."""
-
- return self.int()
-
- def broadcast(self):
- """Return the broadcast (last) address of a network as an (long) integer.
-
- The same as IP[-1]."""
- return self.int() + self.len() - 1
-
- def _printPrefix(self, want):
- """Prints Prefixlen/Netmask.
-
- Not really. In fact it is our universal Netmask/Prefixlen printer.
- This is considered an internel function.
-
- want == 0 / None don't return anything 1.2.3.0
- want == 1 /prefix 1.2.3.0/24
- want == 2 /netmask 1.2.3.0/255.255.255.0
- want == 3 -lastip 1.2.3.0-1.2.3.255
- """
-
- if (self._ipversion == 4 and self._prefixlen == 32) or \
- (self._ipversion == 6 and self._prefixlen == 128):
- if self.NoPrefixForSingleIp:
- want = 0
- if want == None:
- want = self.WantPrefixLen
- if want == None:
- want = 1
- if want:
- if want == 2:
- # this should work wit IP and IPint
- netmask = self.netmask()
- if type(netmask) != types.IntType and type(netmask) != types.LongType:
- netmask = netmask.int()
- return "/%s" % (intToIp(netmask, self._ipversion))
- elif want == 3:
- return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion))
- else:
- # default
- return "/%d" % (self._prefixlen)
- else:
- return ''
-
- # We have different Favours to convert to:
- # strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa
- # strNormal 127.0.0.1 2001:658:22a:cafe:200:c0ff:fe8d:08fa
- # strCompressed 127.0.0.1 2001:658:22a:cafe::1
- # strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA
- # strDec 2130706433 42540616829182469433547974687817795834
-
- def strBin(self, wantprefixlen = None):
- """Return a string representation as a binary value.
-
- >>> print IP('127.0.0.1').strBin()
- 01111111000000000000000000000001
- """
-
-
- if self._ipversion == 4:
- bits = 32
- elif self._ipversion == 6:
- bits = 128
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
- if self.WantPrefixLen == None and wantprefixlen == None:
- wantprefixlen = 0
- ret = _intToBin(self.ip)
- return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
-
- def strCompressed(self, wantprefixlen = None):
- """Return a string representation in compressed format using '::' Notation.
-
- >>> print IP('127.0.0.1').strCompressed()
- 127.0.0.1
- >>> print IP('2001:0658:022a:cafe:0200::1').strCompressed()
- 2001:658:22a:cafe:200::1
- """
-
- if self.WantPrefixLen == None and wantprefixlen == None:
- wantprefixlen = 1
-
- if self._ipversion == 4:
- return self.strFullsize(wantprefixlen)
- else:
- # find the longest sequence of '0'
- hextets = [int(x, 16) for x in self.strFullsize(0).split(':')]
- # every element of followingzeros will contain the number of zeros
- # following the corrospondending element of hextetes
- followingzeros = [0] * 8
- for i in range(len(hextets)):
- followingzeros[i] = _countFollowingZeros(hextets[i:])
- # compressionpos is the position where we can start removing zeros
- compressionpos = followingzeros.index(max(followingzeros))
- if max(followingzeros) > 1:
- # genererate string with the longest number of zeros cut out
- # now we need hextets as strings
- hextets = [x for x in self.strNormal(0).split(':')]
- while compressionpos < len(hextets) and hextets[compressionpos] == '0':
- del(hextets[compressionpos])
- hextets.insert(compressionpos, '')
- if compressionpos + 1 >= len(hextets):
- hextets.append('')
- if compressionpos == 0:
- hextets = [''] + hextets
- return ':'.join(hextets) + self._printPrefix(wantprefixlen)
- else:
- return self.strNormal() + self._printPrefix(wantprefixlen)
-
- def strNormal(self, wantprefixlen = None):
- """Return a string representation in the usual format.
-
- >>> print IP('127.0.0.1').strNormal()
- 127.0.0.1
- >>> print IP('2001:0658:022a:cafe:0200::1').strNormal()
- 2001:658:22a:cafe:200:0:0:1
- """
-
- if self.WantPrefixLen == None and wantprefixlen == None:
- wantprefixlen = 1
-
- if self._ipversion == 4:
- ret = self.strFullsize(0)
- elif self._ipversion == 6:
- ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]])
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
-
-
- return ret + self._printPrefix(wantprefixlen)
-
- def strFullsize(self, wantprefixlen = None):
- """Return a string representation in the non mangled format.
-
- >>> print IP('127.0.0.1').strFullsize()
- 127.0.0.1
- >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize()
- 2001:0658:022a:cafe:0200:0000:0000:0001
- """
-
- if self.WantPrefixLen == None and wantprefixlen == None:
- wantprefixlen = 1
-
- return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
-
- def strHex(self, wantprefixlen = None):
- """Return a string representation in hex format.
-
- >>> print IP('127.0.0.1').strHex()
- 0x7F000001
- >>> print IP('2001:0658:022a:cafe:0200::1').strHex()
- 0x20010658022ACAFE0200000000000001
- """
-
- if self.WantPrefixLen == None and wantprefixlen == None:
- wantprefixlen = 0
-
- x = hex(self.ip)
- if x[-1] == 'L':
- x = x[:-1]
- return x + self._printPrefix(wantprefixlen)
-
- def strDec(self, wantprefixlen = None):
- """Return a string representation in decimal format.
-
- >>> print IP('127.0.0.1').strDec()
- 2130706433
- >>> print IP('2001:0658:022a:cafe:0200::1').strDec()
- 42540616829182469433547762482097946625
- """
-
- if self.WantPrefixLen == None and wantprefixlen == None:
- wantprefixlen = 0
-
- x = str(self.ip)
- if x[-1] == 'L':
- x = x[:-1]
- return x + self._printPrefix(wantprefixlen)
-
- def iptype(self):
- """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc).
-
- >>> print IP('127.0.0.1').iptype()
- PRIVATE
- >>> print IP('192.168.1.1').iptype()
- PRIVATE
- >>> print IP('195.185.1.2').iptype()
- PUBLIC
- >>> print IP('::1').iptype()
- LOOPBACK
- >>> print IP('2001:0658:022a:cafe:0200::1').iptype()
- ASSIGNABLE RIPE
-
- The type information for IPv6 is out of sync with reality.
- """
-
- # this could be greatly improved
-
- if self._ipversion == 4:
- iprange = IPv4ranges
- elif self._ipversion == 6:
- iprange = IPv6ranges
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
- bits = self.strBin()
- for i in range(len(bits), 0, -1):
- if iprange.has_key(bits[:i]):
- return iprange[bits[:i]]
- return "unknown"
-
-
- def netmask(self):
- """Return netmask as an integer.
-
- >>> print hex(IP('195.185.0.0/16').netmask().int())
- 0xFFFF0000L
- """
-
- # TODO: unify with prefixlenToNetmask?
- if self._ipversion == 4:
- locallen = 32 - self._prefixlen
- elif self._ipversion == 6:
- locallen = 128 - self._prefixlen
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
- return ((2L ** self._prefixlen) - 1) << locallen
-
-
- def strNetmask(self):
- """Return netmask as an string. Mostly useful for IPv6.
-
- >>> print IP('195.185.0.0/16').strNetmask()
- 255.255.0.0
- >>> print IP('2001:0658:022a:cafe::0/64').strNetmask()
- /64
- """
-
- # TODO: unify with prefixlenToNetmask?
- if self._ipversion == 4:
- locallen = 32 - self._prefixlen
- return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4)
- elif self._ipversion == 6:
- locallen = 128 - self._prefixlen
- return "/%d" % self._prefixlen
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
- def len(self):
- """Return the length of an subnet.
-
- >>> print IP('195.185.1.0/28').len()
- 16
- >>> print IP('195.185.1.0/24').len()
- 256
- """
-
- if self._ipversion == 4:
- locallen = 32 - self._prefixlen
- elif self._ipversion == 6:
- locallen = 128 - self._prefixlen
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
- return 2L ** locallen
-
-
- def __len__(self):
- """Return the length of an subnet.
-
- Called to implement the built-in function len().
- It breaks with IPv6 Networks. Anybody knows how to fix this."""
-
- # Python < 2.2 has this silly restriction which breaks IPv6
- # how about Python >= 2.2 ... ouch - it presists!
-
- return int(self.len())
-
-
- def __getitem__(self, key):
- """Called to implement evaluation of self[key].
-
- >>> ip=IP('127.0.0.0/30')
- >>> for x in ip:
- ... print hex(x.int())
- ...
- 0x7F000000L
- 0x7F000001L
- 0x7F000002L
- 0x7F000003L
- >>> hex(ip[2].int())
- '0x7F000002L'
- >>> hex(ip[-1].int())
- '0x7F000003L'
- """
-
- if type(key) != types.IntType and type(key) != types.LongType:
- raise TypeError
- if abs(key) >= self.len():
- raise IndexError
- if key < 0:
- key = self.len() - abs(key)
-
- return self.ip + long(key)
-
-
-
- def __contains__(self, item):
- """Called to implement membership test operators.
-
- Should return true if item is in self, false otherwise. Item
- can be other IP-objects, strings or ints.
-
- >>> print IP('195.185.1.1').strHex()
- 0xC3B90101
- >>> 0xC3B90101L in IP('195.185.1.0/24')
- 1
- >>> '127.0.0.1' in IP('127.0.0.0/24')
- 1
- >>> IP('127.0.0.0/24') in IP('127.0.0.0/25')
- 0
- """
-
- item = IP(item)
- if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1:
- return 1
- else:
- return 0
-
-
- def overlaps(self, item):
- """Check if two IP address ranges overlap.
-
- Returns 0 if the two ranged don't overlap, 1 if the given
- range overlaps at the end and -1 if it does at the beginning.
-
- >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24')
- 1
- >>> IP('192.168.0.0/23').overlaps('192.168.1.255')
- 1
- >>> IP('192.168.0.0/23').overlaps('192.168.2.0')
- 0
- >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23')
- -1
- """
-
- item = IP(item)
- if item.ip >= self.ip and item.ip < self.ip + self.len():
- return 1
- elif self.ip >= item.ip and self.ip < item.ip + item.len():
- return -1
- else:
- return 0
-
-
- def __str__(self):
- """Dispatch to the prefered String Representation.
-
- Used to implement str(IP)."""
-
- return self.strFullsize()
-
-
- def __repr__(self):
- """Print a representation of the Object.
-
- Used to implement repr(IP). Returns a string which evaluates
- to an identical Object (without the wnatprefixlen stuff - see
- module docstring.
-
- >>> print repr(IP('10.0.0.0/24'))
- IP('10.0.0.0/24')
- """
-
- return("IPint('%s')" % (self.strCompressed(1)))
-
-
- def __cmp__(self, other):
- """Called by comparison operations.
-
- Should return a negative integer if self < other, zero if self
- == other, a positive integer if self > other.
-
- Networks with different prefixlen are considered non-equal.
- Networks with the same prefixlen and differing addresses are
- considered non equal but are compared by thair base address
- integer value to aid sorting of IP objects.
-
- The Version of Objects is not put into consideration.
-
- >>> IP('10.0.0.0/24') > IP('10.0.0.0')
- 1
- >>> IP('10.0.0.0/24') < IP('10.0.0.0')
- 0
- >>> IP('10.0.0.0/24') < IP('12.0.0.0/24')
- 1
- >>> IP('10.0.0.0/24') > IP('12.0.0.0/24')
- 0
-
- """
-
- # Im not really sure if this is "the right thing to do"
- if self._prefixlen < other.prefixlen():
- return (other.prefixlen() - self._prefixlen)
- elif self._prefixlen > other.prefixlen():
-
- # Fixed bySamuel Krempp <krempp@crans.ens-cachan.fr>:
-
- # The bug is quite obvious really (as 99% bugs are once
- # spotted, isn't it ? ;-) Because of precedence of
- # multiplication by -1 over the substraction, prefixlen
- # differences were causing the __cmp__ function to always
- # return positive numbers, thus the function was failing
- # the basic assumptions for a __cmp__ function.
-
- # Namely we could have (a > b AND b > a), when the
- # prefixlen of a and b are different. (eg let
- # a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything
- # could happen when launching a sort algorithm..
- # everything's in order with the trivial, attached patch.
-
- return (self._prefixlen - other.prefixlen()) * -1
- else:
- if self.ip < other.ip:
- return -1
- elif self.ip > other.ip:
- return 1
- else:
- return 0
-
-
- def __hash__(self):
- """Called for the key object for dictionary operations, and by
- the built-in function hash() Should return a 32-bit integer
- usable as a hash value for dictionary operations. The only
- required property is that objects which compare equal have the
- same hash value
-
- >>> hex(IP('10.0.0.0/24').__hash__())
- '0xf5ffffe7'
- """
-
- thehash = int(-1)
- ip = self.ip
- while ip > 0:
- thehash = (thehash & 0xffffffff) ^ (ip & 0x7fffffff)
- ip = ip >> 32
- thehash = thehash ^ self._prefixlen
- return int(thehash)
-
-
-class IP(IPint):
- """Class for handling IP Addresses and Networks."""
-
- def net(self):
- """Return the base (first) address of a network as an IP object.
-
- The same as IP[0].
-
- >>> IP('10.0.0.0/8').net()
- IP('10.0.0.0')
- """
- return IP(IPint.net(self))
-
- def broadcast(self):
- """Return the broadcast (last) address of a network as an IP object.
-
- The same as IP[-1].
-
- >>> IP('10.0.0.0/8').broadcast()
- IP('10.255.255.255')
- """
- return IP(IPint.broadcast(self))
-
- def netmask(self):
- """Return netmask as an IP object.
-
- >>> IP('10.0.0.0/8').netmask()
- IP('255.0.0.0')
- """
- return IP(IPint.netmask(self))
-
-
- def reverseNames(self):
- """Return a list with values forming the reverse lookup.
-
- >>> IP('213.221.113.87/32').reverseNames()
- ['87.113.221.213.in-addr.arpa.']
- >>> IP('213.221.112.224/30').reverseNames()
- ['224.112.221.213.in-addr.arpa.', '225.112.221.213.in-addr.arpa.', '226.112.221.213.in-addr.arpa.', '227.112.221.213.in-addr.arpa.']
- >>> IP('127.0.0.0/24').reverseNames()
- ['0.0.127.in-addr.arpa.']
- >>> IP('127.0.0.0/23').reverseNames()
- ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.']
- >>> IP('127.0.0.0/16').reverseNames()
- ['0.127.in-addr.arpa.']
- >>> IP('127.0.0.0/15').reverseNames()
- ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.']
- >>> IP('128.0.0.0/8').reverseNames()
- ['128.in-addr.arpa.']
- >>> IP('128.0.0.0/7').reverseNames()
- ['128.in-addr.arpa.', '129.in-addr.arpa.']
-
- """
-
- if self._ipversion == 4:
- ret =[]
- # TODO: Refactor. Add support for IPint objects
- if self.len() < 2**8:
- for x in self:
- ret.append(x.reverseName())
- elif self.len() < 2**16L:
- for i in range(0, self.len(), 2**8):
- ret.append(self[i].reverseName()[2:])
- elif self.len() < 2**24L:
- for i in range(0, self.len(), 2**16):
- ret.append(self[i].reverseName()[4:])
- else:
- for i in range(0, self.len(), 2**24):
- ret.append(self[i].reverseName()[6:])
- return ret
- elif self._ipversion == 6:
- s = hex(self.ip)[2:].lower()
- if s[-1] == 'l':
- s = s[:-1]
- if self._prefixlen % 4 != 0:
- raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level"
- s = list(s)
- s.reverse()
- s = '.'.join(s)
- first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
- return ["%s.ip6.int." % s[first_nibble_index:]]
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
-
-
- def reverseName(self):
- """Return the value for reverse lookup/PTR records as RfC 2317 look alike.
-
- RfC 2317 is an ugly hack which only works for sub-/24 e.g. not
- for /23. Do not use it. Better set up a Zone for every
- address. See reverseName for a way to arcive that.
-
- >>> print IP('195.185.1.1').reverseName()
- 1.1.185.195.in-addr.arpa.
- >>> print IP('195.185.1.0/28').reverseName()
- 0-15.1.185.195.in-addr.arpa.
- """
-
- if self._ipversion == 4:
- s = self.strFullsize(0)
- s = s.split('.')
- s.reverse()
- first_byte_index = int(4 - (self._prefixlen / 8))
- if self._prefixlen % 8 != 0:
- nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1])
- if nibblepart[-1] == 'l':
- nibblepart = nibblepart[:-1]
- nibblepart += '.'
- else:
- nibblepart = ""
-
- s = '.'.join(s[first_byte_index:])
- return "%s%s.in-addr.arpa." % (nibblepart, s)
-
- elif self._ipversion == 6:
- s = hex(self.ip)[2:].lower()
- if s[-1] == 'l':
- s = s[:-1]
- if self._prefixlen % 4 != 0:
- nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower())
- if nibblepart[-1] == 'l':
- nibblepart = nibblepart[:-1]
- nibblepart += '.'
- else:
- nibblepart = ""
- s = list(s)
- s.reverse()
- s = '.'.join(s)
- first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
- return "%s%s.ip6.int." % (nibblepart, s[first_nibble_index:])
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
- def __getitem__(self, key):
- """Called to implement evaluation of self[key].
-
- >>> ip=IP('127.0.0.0/30')
- >>> for x in ip:
- ... print str(x)
- ...
- 127.0.0.0
- 127.0.0.1
- 127.0.0.2
- 127.0.0.3
- >>> print str(ip[2])
- 127.0.0.2
- >>> print str(ip[-1])
- 127.0.0.3
- """
- return IP(IPint.__getitem__(self, key))
-
- def __repr__(self):
- """Print a representation of the Object.
-
- >>> IP('10.0.0.0/8')
- IP('10.0.0.0/8')
- """
-
- return("IP('%s')" % (self.strCompressed(1)))
-
- def __add__(self, other):
- """Emulate numeric objects through network aggregation"""
- if self.prefixlen() != other.prefixlen():
- raise ValueError, "Only networks with the same prefixlen can be added."
- if self.prefixlen < 1:
- raise ValueError, "Networks with a prefixlen longer than /1 can't be added."
- if self.version() != other.version():
- raise ValueError, "Only networks with the same IP version can be added."
- if self > other:
- # fixed by Skinny Puppy <skin_pup-IPy@happypoo.com>
- return other.__add__(self)
- else:
- ret = IP(self.int())
- ret._prefixlen = self.prefixlen() - 1
- return ret
-
-def parseAddress(ipstr):
- """Parse a string and return the corrospondending IPaddress and the a guess of the IP version.
-
- Following Forms ar recorgnized:
- 0x0123456789abcdef # IPv4 if <= 0xffffffff else IPv6
- 123.123.123.123 # IPv4
- 123.123 # 0-padded IPv4
- 1080:0000:0000:0000:0008:0800:200C:417A
- 1080:0:0:0:8:800:200C:417A
- 1080:0::8:800:200C:417A
- ::1
- ::
- 0:0:0:0:0:FFFF:129.144.52.38
- ::13.1.68.3
- ::FFFF:129.144.52.38
- """
-
- # TODO: refactor me!
- if ipstr.startswith('0x'):
- ret = long(ipstr[2:], 16)
- if ret > 0xffffffffffffffffffffffffffffffffL:
- raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr)
- if ret < 0x100000000L:
- return (ret, 4)
- else:
- return (ret, 6)
-
- if ipstr.find(':') != -1:
- # assume IPv6
- if ipstr.find(':::') != -1:
- raise ValueError, "%r: IPv6 Address can't contain ':::'" % (ipstr)
- hextets = ipstr.split(':')
- if ipstr.find('.') != -1:
- # this might be a mixed address like '0:0:0:0:0:0:13.1.68.3'
- (v4, foo) = parseAddress(hextets[-1])
- assert foo == 4
- del(hextets[-1])
- hextets.append(hex(v4 >> 16)[2:-1])
- hextets.append(hex(v4 & 0xffff)[2:-1])
- if len(hextets) > 8:
- raise ValueError, "%r: IPv6 Address with more than 8 hexletts" % (ipstr)
- if len(hextets) < 8:
- if '' not in hextets:
- raise ValueError, "%r IPv6 Address with less than 8 hexletts and without '::'" % (ipstr)
- # catch :: at the beginning or end
- if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
- hextets.remove('')
- # catch '::'
- if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
- hextets.remove('')
-
- for foo in range(9-len(hextets)):
- hextets.insert(hextets.index(''), '0')
- hextets.remove('')
- if '' in hextets:
- raise ValueError, "%r IPv6 Address may contain '::' only once" % (ipstr)
- if '' in hextets:
- raise ValueError, "%r IPv6 Address may contain '::' only if it has less than 8 hextets" % (ipstr)
- num = ''
- for x in hextets:
- if len(x) < 4:
- x = ((4 - len(x)) * '0') + x
- if int(x, 16) < 0 or int(x, 16) > 0xffff:
- raise ValueError, "%r: single hextet must be 0 <= hextet <= 0xffff which isn't true for %s" % (ipstr, x)
- num += x
- return (long(num, 16), 6)
-
- elif len(ipstr) == 32:
- # assume IPv6 in pure hexadecimal notation
- return (long(ipstr, 16), 6)
-
- elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256):
- # assume IPv4 ('127' gets interpreted as '127.0.0.0')
- bytes = ipstr.split('.')
- if len(bytes) > 4:
- raise ValueError, "IPv4 Address with more than 4 bytes"
- bytes += ['0'] * (4 - len(bytes))
- bytes = [long(x) for x in bytes]
- for x in bytes:
- if x > 255 or x < 0:
- raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr)
- return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4)
-
- else:
- # we try to interprete it as a decimal digit -
- # this ony works for numbers > 255 ... others
- # will be interpreted as IPv4 first byte
- ret = long(ipstr)
- if ret > 0xffffffffffffffffffffffffffffffffL:
- raise ValueError, "IP Address cant be bigger than 2^128"
- if ret <= 0xffffffffL:
- return (ret, 4)
- else:
- return (ret, 6)
-
-
-def intToIp(ip, version):
- """Transform an integer string into an IP address."""
-
- # just to be sure and hoping for Python 2.22
- ip = long(ip)
-
- if ip < 0:
- raise ValueError, "IPs can't be negative: %d" % (ip)
-
- ret = ''
- if version == 4:
- if ip > 0xffffffffL:
- raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip))
- for l in range(4):
- ret = str(ip & 0xffL) + '.' + ret
- ip = ip >> 8;
- ret = ret[:-1]
- elif version == 6:
- if ip > 0xffffffffffffffffffffffffffffffffL:
- raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip))
- l = '0' * 32 + hex(ip)[2:-1]
- for x in range(1,33):
- ret = l[-x] + ret
- if x % 4 == 0:
- ret = ':' + ret
- ret = ret[1:]
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
- return ret;
-
-def _ipVersionToLen(version):
- """Return number of bits in address for a certain IP version.
-
- >>> _ipVersionToLen(4)
- 32
- >>> _ipVersionToLen(6)
- 128
- >>> _ipVersionToLen(5)
- Traceback (most recent call last):
- File "<stdin>", line 1, in ?
- File "IPy.py", line 1076, in _ipVersionToLen
- raise ValueError, "only IPv4 and IPv6 supported"
- ValueError: only IPv4 and IPv6 supported
- """
-
- if version == 4:
- return 32
- elif version == 6:
- return 128
- else:
- raise ValueError, "only IPv4 and IPv6 supported"
-
-
-def _countFollowingZeros(l):
- """Return Nr. of elements containing 0 at the beginning th the list."""
- if len(l) == 0:
- return 0
- elif l[0] != 0:
- return 0
- else:
- return 1 + _countFollowingZeros(l[1:])
-
-
-_BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
- '4': '0100', '5': '0101', '6': '0110', '7': '0111',
- '8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
- 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
-
-def _intToBin(val):
- """Return the binary representation of an integer as string."""
-
- if val < 0:
- raise ValueError, "Only positive Values allowed"
- s = hex(val).lower()
- ret = ''
- if s[-1] == 'l':
- s = s[:-1]
- for x in s[2:]:
- if __debug__:
- if not _BitTable.has_key(x):
- raise AssertionError, "hex() returned strange result"
- ret += _BitTable[x]
- # remove leading zeros
- while ret[0] == '0' and len(ret) > 1:
- ret = ret[1:]
- return ret
-
-def _count1Bits(num):
- """Find the highest bit set to 1 in an integer."""
- ret = 0
- while num > 0:
- num = num >> 1
- ret += 1
- return ret
-
-def _count0Bits(num):
- """Find the highest bit set to 0 in an integer."""
-
- # this could be so easy if _count1Bits(~long(num)) would work as excepted
- num = long(num)
- if num < 0:
- raise ValueError, "Only positive Numbers please: %s" % (num)
- ret = 0
- while num > 0:
- if num & 1 == 1:
- break
- num = num >> 1
- ret += 1
- return ret
-
-
-def _checkPrefix(ip, prefixlen, version):
- """Check the validity of a prefix
-
- Checks if the variant part of a prefix only has 0s, and the length is
- correct.
-
- >>> _checkPrefix(0x7f000000L, 24, 4)
- 1
- >>> _checkPrefix(0x7f000001L, 24, 4)
- 0
- >>> repr(_checkPrefix(0x7f000001L, -1, 4))
- 'None'
- >>> repr(_checkPrefix(0x7f000001L, 33, 4))
- 'None'
- """
-
- # TODO: unify this v4/v6/invalid code in a function
- bits = _ipVersionToLen(version)
-
- if prefixlen < 0 or prefixlen > bits:
- return None
-
- if ip == 0:
- zbits = bits + 1
- else:
- zbits = _count0Bits(ip)
- if zbits < bits - prefixlen:
- return 0
- else:
- return 1
-
-
-def _checkNetmask(netmask, masklen):
- """Checks if a netmask is expressable as e prefixlen."""
-
- num = long(netmask)
- bits = masklen
-
- # remove zero bits at the end
- while (num & 1) == 0:
- num = num >> 1
- bits -= 1
- if bits == 0:
- break
- # now check if the rest consists only of ones
- while bits > 0:
- if (num & 1) == 0:
- raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask))
- num = num >> 1
- bits -= 1
-
-
-def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
- """Check if a base addess of e network is compatible with a prefixlen"""
- if net & _prefixlenToNetmask(prefixlen, version) == net:
- return 1
- else:
- return 0
-
-
-def _netmaskToPrefixlen(netmask):
- """Convert an Integer reprsenting a Netmask to an prefixlen.
-
- E.g. 0xffffff00 (255.255.255.0) returns 24
- """
-
- netlen = _count0Bits(netmask)
- masklen = _count1Bits(netmask)
- _checkNetmask(netmask, masklen)
- return masklen - netlen
-
-
-def _prefixlenToNetmask(prefixlen, version):
- """Return a mask of n bits as a long integer.
-
- From 'IP address conversion functions with the builtin socket module' by Alex Martelli
- http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517
- """
- if prefixlen == 0:
- return 0
- elif prefixlen < 0:
- raise ValueError, "Prefixlen must be > 0"
- return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
-
-
-def _test():
- import doctest, IPy
- return doctest.testmod(IPy)
-
-if __name__ == "__main__":
- _test()
-
- t = [0xf0, 0xf00, 0xff00, 0xffff00, 0xffffff00L]
- o = []
- for x in t:
- pass
- x = 0L
diff --git a/cobbler/api.py b/cobbler/api.py
index 0c0141f..d93bf54 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -17,57 +17,44 @@ from msg import *
class BootAPI:
+ _config = config.Config()
def __init__(self):
"""
Constructor...
- """
- # if the file already exists, load real data now
- try:
- if config.files_exist():
- config.deserialize()
- except Exception, e:
- # parse errors, attempt to recover
- print runtime.last_error()
- if runtime.last_error() == m("parse_error"):
- # it's bad, raise it so we can croak
- raise Exception, "parse_error"
- try:
- config.serialize()
- except:
- # it's bad, raise it so we can croak
- traceback.print_exc()
- raise Exception, "parse_error2"
- if not config.files_exist():
- config.serialize()
+ """
+
+ # FIXME: deserializer/serializer error
+ # handling probably not up to par yet
+ _config.deserialize()
def clear(self):
"""
Forget about current list of profiles, distros, and systems
"""
- config.clear()
+ _config.clear()
- def get_systems(self):
+ def systems(self):
"""
Return the current list of systems
"""
- return config.get_systems()
+ return _config.systems()
- def get_profiles(self):
+ def profiles(self):
"""
Return the current list of profiles
"""
- return config.get_profiles()
+ return _config.profiles()
- def get_distros(self):
+ def distros(self):
"""
Return the current list of distributions
"""
- return config.get_distros()
+ return _config.distros()
def new_system(self):
@@ -100,7 +87,7 @@ class BootAPI:
for human admins, who may, for instance, forget to properly set up
their TFTP servers for PXE, etc.
"""
- return check.bootcheck().run()
+ return check.bootcheck(_config).run()
def sync(self,dry_run=True):
@@ -110,19 +97,19 @@ class BootAPI:
/tftpboot. Any operations done in the API that have not been
saved with serialize() will NOT be synchronized with this command.
"""
- config.deserialize();
- return sync.bootsync(self).sync(dry_run)
+ # config.deserialize(); # neccessary?
+ return sync.bootsync(_config).sync(dry_run)
def serialize(self):
"""
Save the config file(s) to disk.
"""
- config.serialize()
+ _config.serialize()
def deserialize(self):
"""
Load the current configuration from config file(s)
"""
- config.deserialize()
+ _config.deserialize()
diff --git a/cobbler/check.py b/cobbler/check.py
index ce0fdaf..fd3291f 100644
--- a/cobbler/check.py
+++ b/cobbler/check.py
@@ -5,16 +5,15 @@
import os
import sys
import re
-import weakref
+import config
from msg import *
class BootCheck:
- def __init__(self, api):
- self.api = weakref.proxy(api)
- self.config = weakref.proxy(self.api.config)
-
+ def __init__(self,config):
+ self.config = config
+ self.settings = config.settings()
def run(self):
"""
@@ -34,11 +33,11 @@ class BootCheck:
return status
def check_name(self,status):
- if self.config.server == "localhost":
+ if self.settings.server == "localhost":
status.append(m("bad_server"))
def check_httpd(self,status):
- if not os.path.exists(self.config.httpd_bin):
+ if not os.path.exists(self.settings.httpd_bin):
status.append(m("no_httpd"))
@@ -46,29 +45,29 @@ class BootCheck:
"""
Check if dhcpd is installed
"""
- if not os.path.exists(self.config.dhcpd_bin):
+ if not os.path.exists(self.settings.dhcpd_bin):
status.append(m("no_dhcpd"))
def check_pxelinux_bin(self,status):
"""
Check if pxelinux (part of syslinux) is installed
"""
- if not os.path.exists(self.config.pxelinux):
+ if not os.path.exists(self.settings.pxelinux):
status.append(m("no_pxelinux"))
def check_tftpd_bin(self,status):
"""
Check if tftpd is installed
"""
- if not os.path.exists(self.config.tftpd_bin):
+ if not os.path.exists(self.settings.tftpd_bin):
status.append(m("no_tftpd"))
def check_tftpd_dir(self,status):
"""
Check if cobbler.conf's tftpboot directory exists
"""
- if not os.path.exists(self.config.tftpboot):
- status.append(m("no_dir") % self.config.tftpboot)
+ if not os.path.exists(self.settings.tftpboot):
+ status.append(m("no_dir") % self.settings.tftpboot)
def check_tftpd_conf(self,status):
@@ -76,22 +75,22 @@ class BootCheck:
Check that configured tftpd boot directory matches with actual
Check that tftpd is enabled to autostart
"""
- if os.path.exists(self.config.tftpd_conf):
- f = open(self.config.tftpd_conf)
+ if os.path.exists(self.settings.tftpd_conf):
+ f = open(self.settings.tftpd_conf)
re_1 = re.compile(r'default:.*off')
re_2 = re.compile(r'disable.*=.*yes')
found_bootdir = False
for line in f.readlines():
if re_1.search(line):
- status.append(m("chg_attrib") % ('default','on',self.config.tftpd_conf))
+ status.append(m("chg_attrib") % ('default','on',self.settings.tftpd_conf))
if re_2.search(line):
- status.append(m("chg_attrib") % ('disable','no',self.config.tftpd_conf))
- if line.find("-s %s" % self.config.tftpboot) != -1:
+ status.append(m("chg_attrib") % ('disable','no',self.settings.tftpd_conf))
+ if line.find("-s %s" % self.settings.tftpboot) != -1:
found_bootdir = True
if not found_bootdir:
- status.append(m("chg_attrib") % ('server_args',"-s %s" % self.config.tftpboot, self.config.tftpd_conf))
+ status.append(m("chg_attrib") % ('server_args',"-s %s" % self.settings.tftpboot, self.settings.tftpd_conf))
else:
- status.append(m("no_exist") % self.config.tftpd_conf)
+ status.append(m("no_exist") % self.settings.tftpd_conf)
def check_dhcpd_conf(self,status):
@@ -99,20 +98,20 @@ class BootCheck:
Check that dhcpd *appears* to be configured for pxe booting.
We can't assure file correctness
"""
- if os.path.exists(self.config.dhcpd_conf):
+ if os.path.exists(self.settings.dhcpd_conf):
match_next = False
match_file = False
- f = open(self.config.dhcpd_conf)
+ f = open(self.settings.dhcpd_conf)
for line in f.readlines():
if line.find("next-server") != -1:
match_next = True
if line.find("filename") != -1:
match_file = True
if not match_next:
- status.append(m("no_line") % (self.config.dhcpd_conf, 'next-server ip-address'))
+ status.append(m("no_line") % (self.settings.dhcpd_conf, 'next-server ip-address'))
if not match_file:
- status.append(m("no_line") % (self.config.dhcpd_conf, 'filename "%s/pxelinux.0";' % self.config.tftpboot))
+ status.append(m("no_line") % (self.settings.dhcpd_conf, 'filename "%s/pxelinux.0";' % self.settings.tftpboot))
else:
- status.append(m("no_exist") % self.config.dhcpd_conf)
+ status.append(m("no_exist") % self.settings.dhcpd_conf)
diff --git a/cobbler/collection.py b/cobbler/collection.py
index 196bf7d..5a008f7 100644
--- a/cobbler/collection.py
+++ b/cobbler/collection.py
@@ -2,20 +2,15 @@
"""
Base class for any serializable lists of things...
"""
-class Collection:
+class Collection(Serializable):
_item_factory = None
+ _filename = None
def __init__(self):
"""
- Constructor. Requires an API reference. seed_data
- is a hash of data to feed into the collection, that would
- come from the config file in /var.
+ Constructor.
"""
self.listing = {}
- # no longer done at construct time, use from_datastruct
- #if seed_data is not None:
- # for x in seed_data:
- # self.add(self._item_factory(self.api, x))
def find(self,name):
"""
@@ -29,11 +24,9 @@ class Collection:
def to_datastruct(self):
"""
- Return datastructure representation of this collection suitable
- for feeding to a serializer (such as YAML)
+ Serialize the collection
"""
- return [x.to_datastruct() for x in self.listing.values()]
-
+ datastruct = [x.to_datastruct() for x in self.listing.values()]
def from_datastruct(self,datastruct):
for x in datastruct:
@@ -66,14 +59,6 @@ class Collection:
else:
return m("empty_list")
- #def contents(self):
- # """
- # Access the raw contents of the collection. Classes shouldn't
- # be doing this (preferably) and should use the __iter__ interface.
- # Deprecrated.
- # """
- # return self.listing.values()
-
def __iter__(self):
"""
Iterator for the collection. Allows list comprehensions, etc
diff --git a/cobbler/distros.py b/cobbler/distros.py
index a6b4b14..a510fd0 100644
--- a/cobbler/distros.py
+++ b/cobbler/distros.py
@@ -8,6 +8,7 @@ and initrd files
"""
class Distros(Collection):
_item_factory = distro.Distro
+ _filename = "/var/lib/cobbler/distros"
def remove(self,name):
"""
diff --git a/cobbler/item.py b/cobbler/item.py
index e30ada5..0d43d9f 100644
--- a/cobbler/item.py
+++ b/cobbler/item.py
@@ -3,6 +3,12 @@ An Item is a serializable thing that can appear in a Collection
"""
class Item:
+ """
+ constructor must be of format:
+ def __init__(self,seed_data)
+ where seed_data is a hash of argument_name/value pairs
+ see profile.py for example
+ """
def set_name(self,name):
"""
@@ -35,3 +41,5 @@ class Item:
items been set, are they free of conflicts, etc.
"""
return False
+
+
diff --git a/cobbler/profile.py b/cobbler/profile.py
index 19db0e8..09c91bf 100644
--- a/cobbler/profile.py
+++ b/cobbler/profile.py
@@ -1,7 +1,6 @@
class Profile(Item):
- def __init__(self,api,seed_data):
- self.api = api
+ def __init__(self,seed_data):
self.name = None
self.distro = None # a name, not a reference
self.kickstart = None
diff --git a/cobbler/profiles.py b/cobbler/profiles.py
index c73e8c3..9fb2d92 100644
--- a/cobbler/profiles.py
+++ b/cobbler/profiles.py
@@ -11,6 +11,7 @@ additional options, with client-side defaults (not kept here).
"""
class Profiles(Collection):
_item_factory = profile.Profile
+ _filename = "/var/lib/cobbler/profiles"
def remove(self,name):
"""
diff --git a/cobbler/serializable.py b/cobbler/serializable.py
new file mode 100644
index 0000000..e03b88f
--- /dev/null
+++ b/cobbler/serializable.py
@@ -0,0 +1,12 @@
+
+class Serializable:
+
+ def filename():
+ return None
+
+ def from_datastruct(datastruct):
+ raise "not implemented"
+
+ def to_datastruct():
+ raise "not implemented"
+
diff --git a/cobbler/serializer.py b/cobbler/serializer.py
new file mode 100644
index 0000000..847be6e
--- /dev/null
+++ b/cobbler/serializer.py
@@ -0,0 +1,19 @@
+# Michael DeHaan <mdehaan@redhat.com>
+
+import api
+import util
+
+def serialize(obj):
+ fd = open(obj.filename(),"w+")
+ datastruct = obj.to_datastruct()
+ yaml = syck.dump(datastruct)
+ fd.write(yaml)
+ fd.close()
+ return True
+
+def deserialize(obj):
+ fd = open(obj.filename(),"r")
+ data = fd.read()
+ datastruct = yaml.load(data)
+ fd.close()
+ return obj.from_datastruct(datastruct)
diff --git a/cobbler/serializer_tools.py b/cobbler/serializer_tools.py
deleted file mode 100644
index 7c32ce5..0000000
--- a/cobbler/serializer_tools.py
+++ /dev/null
@@ -1,218 +0,0 @@
-# Abstracts out the config file format/access and holds
-# reasonable default settings.
-#
-# Michael DeHaan <mdehaan@redhat.com>
-
-import api
-import util
-from msg import *
-import distros
-import profiles
-import systems
-
-import weakref
-import syck # pysyck > 0.61, so it has dump()
-import os
-import traceback
-
-global_settings_file = "/etc/cobbler.conf"
-global_state_file = "/var/lib/cobbler/cobbler.conf"
-
-
-class BootConfig:
-
- __instance
-
- def __new__(typ, *args, **kwargs):
- if __instance is None:
- __instance = object.__new__(type,*args,**kwargs)
- return __instance
-
- def __init__(self):
- """
- Constructor. This class maintains both the logical
- configuration for Cobbler and the file representation thereof.
- Creating the config object only loads the default values,
- users of this class need to call deserialize() to load config
- file values. See cobbler.py for how the CLI does it.
- """
- self.settings_file = global_settings_file
- self.state_file = global_state_file
- self.set_defaults()
- self.clear()
-
- def files_exist(self):
- """
- Returns whether the config files exist.
- """
- return os.path.exists(self.settings_file) and os.path.exists(self.state_file)
-
- def clear(self):
- """
- Establish an empty list of profiles distros, and systems.
- """
- profiles.clear()
- distros.clear()
- systems.clear()
-
- def set_defaults(self):
- """
- Set some reasonable defaults in case no values are available
- """
- self.server = "localhost"
- self.tftpboot = "/tftpboot"
- self.dhcpd_conf = "/etc/dhcpd.conf"
- self.tftpd_conf = "/etc/xinetd.d/tftp"
- self.pxelinux = "/usr/lib/syslinux/pxelinux.0"
- self.tftpd_bin = "/usr/sbin/in.tftpd"
- self.dhcpd_bin = "/usr/sbin/dhcpd"
- self.httpd_bin = "/usr/sbin/httpd"
- self.kernel_options = "append devfs=nomount ramdisk_size=16438 lang= vga=788 ksdevice=eth0" #initrd and ks added programmatically
-
- def get_profiles(self):
- """
- Access the current profiles list
- """
- return self.profiles
-
- def get_distros(self):
- """
- Access the current distros list
- """
- return self.distros
-
- def get_systems(self):
- """
- Access the current systems list
- """
- return self.systems
-
- def config_to_hash(self):
- """
- Save all global config options in hash form (for serialization)
- """
- data = {}
- data["server"] = self.server
- data['tftpboot'] = self.tftpboot
- data['dhcpd_conf'] = self.dhcpd_conf
- data['tftpd_conf'] = self.tftpd_conf
- data['pxelinux'] = self.pxelinux
- data['tftpd_bin'] = self.tftpd_bin
- data['dhcpd_bin'] = self.dhcpd_bin
- data['httpd_bin'] = self.httpd_bin
- data['kernel_options'] = self.kernel_options
- return data
-
- def config_from_hash(self,hash):
- """
- Load all global config options from hash form (for deserialization)
- """
- try:
- self.server = hash['server']
- self.tftpboot = hash['tftpboot']
- self.dhcpd_conf = hash['dhcpd_conf']
- self.tftpd_conf = hash['tftpd_conf']
- self.pxelinux = hash['pxelinux']
- self.tftpd_bin = hash['tftpd_bin']
- self.dhcpd_bin = hash['dhcpd_bin']
- self.httpd_bin = hash['httpd_bin']
- self.kernel_options = hash['kernel_options']
- except:
- print "WARNING: config file error: %s" % (self.settings_file)
- self.set_defaults()
-
- def to_hash(self,is_etc):
- """
- Convert all items cobbler knows about to a nested hash.
- There are seperate hashes for the /etc and /var portions.
- """
- world = {}
- if is_etc:
- world['config'] = self.config_to_hash()
- else:
- world['distros'] = self.get_distros().to_datastruct()
- world['profiles'] = self.get_profiles().to_datastruct()
- world['systems'] = self.get_systems().to_datastruct()
- return world
-
-
- def from_hash(self,hash,is_etc):
- """
- Convert a hash representation of a cobbler to 'reality'
- There are seperate hashes for the /etc and /var portions.
- """
- if is_etc:
- self.config_from_hash(hash['config'])
- else:
- distros.from_datastruct(hash['distros'])
- profiles.from_datastruct(hash['profiles'])
- systems.from_datastruct(hash['systems'])
-
- # ------------------------------------------------------
- # we don't care about file formats until below this line
-
- def serialize(self):
- """
- Save everything to the config file.
- This goes through an intermediate data format so we
- could use YAML later if we wanted.
- """
-
- settings = None
- state = None
-
- # ------
- # dump internal state (distros, profiles, systems...) into /var/lib/...
- # /etc is not serialized, it's packaged.
-
- if not os.path.isdir(os.path.dirname(self.state_file)):
- dirname = os.path.dirname(self.state_file)
- if dirname != "":
- os.makedirs(os.path.dirname(self.state_file))
- try:
- state = open(self.state_file,"w+")
- except:
- runtime.set_error(m("cant_create: %s" % self.state_file))
- return False
- data = self.to_hash(False)
- state.write(syck.dump(data))
-
- # all good
- return True
-
- def deserialize(self):
- """
- Load everything from the config file.
- This goes through an intermediate data structure format so we
- could use YAML later if we wanted.
- """
-
- # -----
- # load global config (pathing, urls, etc)...
- try:
- settings = syck.load(open(self.settings_file,"r").read())
- if settings is not None:
- self.from_hash(settings,True)
- else:
- self.last_error = m("parse_error")
- raise Exception("parse_error")
- except:
- runtime.set_error("parse_error")
- raise Exception("parse_error")
-
- # -----
- # load internal state(distros, systems, profiles...)
- try:
- state = syck.load(open(self.state_file,"r").read())
- if state is not None:
- self.from_hash(state,False)
- else:
- self.last_error = m("parse_error2")
- raise Exception("parse_error2")
- except:
- runtime.set_error("parse_error2")
- raise Exception("parse_error2")
-
- # all good
- return True
-
diff --git a/cobbler/settings.py b/cobbler/settings.py
new file mode 100644
index 0000000..2a7553f
--- /dev/null
+++ b/cobbler/settings.py
@@ -0,0 +1,34 @@
+"""
+Cobbler app-wide settings
+"""
+
+class Settings(Serializable)
+
+ def filename(self):
+ return "/var/lib/cobbler/settings"
+
+ def __init__(self):
+
+ self._attributes = {
+ "httpd_bin" : "/usr/sbin/httpd",
+ "pxelinux" : "/usr/lib/syslinux/pxelinux.0",
+ "dhcpd_conf" : "/etc/dhcpd.conf",
+ "tftpd_bin" : "/usr/sbin/in.tftpd",
+ "server" : "localhost",
+ "dhcpd_bin" : "/usr/sbin/dhcpd",
+ "kernel_options" : "append devfs=nomount ramdisk_size=16438 lang= vga=788 ksdevice=eth0",
+ "tftpd_conf" : "/etc/xinetd.d/tftp",
+ "tftpboot" : "/tftpboot",
+ }
+
+ def to_datastruct():
+ return self._attributes()
+
+ def from_datastruct(datastruct):
+ self._attributes = datastruct
+
+ def __getattr__(self,name):
+ if name in self._attributes:
+ return self._attributes[name]
+ else:
+ raise AttributeError, name
diff --git a/cobbler/sync.py b/cobbler/sync.py
index 31ab481..c768b57 100644
--- a/cobbler/sync.py
+++ b/cobbler/sync.py
@@ -2,7 +2,6 @@
#
# Michael DeHaan <mdehaan@redhat.com>
-import api
import config
import os
@@ -21,8 +20,13 @@ Handles conversion of internal state to the tftpboot tree layout
class BootSync:
- def __init__(self):
- self.verbose = True
+ def __init__(self,config):
+ self.verbose = True
+ self.config = config
+ self.distros = config.distros
+ self.profiles = config.profiles
+ self.systems = config.systems
+ self.settings = config.settings
def sync(self,dry_run=False,verbose=True):
@@ -48,7 +52,7 @@ class BootSync:
"""
Copy syslinux to the configured tftpboot directory
"""
- self.copy(config.config().pxelinux, os.path.join(config.config().tftpboot, "pxelinux.0"))
+ self.copy(self.settings.pxelinux, os.path.join(self.settings.tftpboot, "pxelinux.0"))
def configure_httpd(self):
"""
@@ -59,7 +63,7 @@ class BootSync:
self.sync_log(m("no_httpd"))
return
f = self.open_file("/etc/httpd/conf.d/cobbler.conf","w+")
- config = """
+ config_data = """
#
# This configuration file allows 'cobbler' boot info
# to be accessed over HTTP in addition to PXE.
@@ -71,8 +75,8 @@ class BootSync:
Allow from all
</Directory>
"""
- config.replace("/tftpboot",config.config().tftpboot)
- self.tee(f, config)
+ config_data.replace("/tftpboot",self.settings.tftpboot)
+ self.tee(f, config_data)
self.close_file(f)
def clean_trees(self):
@@ -80,7 +84,7 @@ class BootSync:
Delete any previously built pxelinux.cfg tree and xen tree info.
"""
for x in ["pxelinux.cfg","images","systems","distros","profiles","kickstarts"]:
- path = os.path.join(config.config().tftpboot,x)
+ path = os.path.join(self.settings.tftpboot,x)
self.rmtree(path, True)
self.mkdir(path)
@@ -93,8 +97,8 @@ class BootSync:
mounted.
"""
# copy is a 4-letter word but tftpboot runs chroot, thus it's required.
- distros = os.path.join(config.config().tftpboot, "images")
- for d in distros.get_distros():
+ distros = os.path.join(self.settings.tftpboot, "images")
+ for d in self.distros:
distro_dir = os.path.join(distros,d.name)
self.mkdir(distro_dir)
kernel = utils.find_kernel(d.kernel) # full path
@@ -123,12 +127,12 @@ class BootSync:
# these are served by either NFS, Apache, or some ftpd, so we don't need to copy them
# it's up to the user to make sure they are nicely served by their URLs
- for g in profiles.get_profiles():
+ for g in self.profiles:
self.sync_log("mirroring any local kickstarts: %s" % g.name)
kickstart_path = utils.find_kickstart(g.kickstart)
if kickstart_path and os.path.exists(kickstart_path):
# the input is an *actual* file, hence we have to copy it
- copy_path = os.path.join(config.config().tftpboot, "kickstarts", g.name)
+ copy_path = os.path.join(self.settings.tftpboot, "kickstarts", g.name)
self.mkdir(copy_path)
dest = os.path.join(copy_path, "ks.cfg")
try:
@@ -147,47 +151,44 @@ class BootSync:
print "building trees..."
# create pxelinux.cfg under tftpboot
# and file for each MAC or IP (hex encoded 01-XX-XX-XX-XX-XX-XX)
- systems = systems.get_systems()
- profiles = profiles.get_profiles()
- distros = distros.get_distros()
- for d in distros:
+ for d in self.distros:
self.sync_log("processing distro: %s" % d.name)
# TODO: add check to ensure all distros have profiles (=warning)
- filename = os.path.join(config.config().tftpboot,"distros",d.name)
+ filename = os.path.join(self.settings.tftpboot,"distros",d.name)
d.kernel_options = self.blend_kernel_options((
- config.config().kernel_options,
+ self.settings.kernel_options,
d.kernel_options
))
self.write_distro_file(filename,d)
- for p in profiles:
+ for p in self.profiles:
self.sync_log("processing profile: %s" % p.name)
# TODO: add check to ensure all profiles have distros (=error)
# TODO: add check to ensure all profiles have systems (=warning)
- filename = os.path.join(config.config().tftpboot,"profiles",p.name)
- distro = distros.get_distros().find(p.distro)
+ filename = os.path.join(self.settings.tftpboot,"profiles",p.name)
+ distro = self.distros.find(p.distro)
if distro is not None:
p.kernel_options = self.blend_kernel_options((
- config.config().kernel_options,
+ self.settings.kernel_options,
distro.kernel_options,
p.kernel_options
))
self.write_profile_file(filename,p)
- for system in systems:
+ for system in self.systems:
self.sync_log("processing system: %s" % system.name)
- profile = profiles.find(system.profile)
+ profile = self.profiles.find(system.profile)
if profile is None:
runtime.set_error("orphan_profile2")
raise "error"
- distro = distros.find(profile.distro)
+ distro = self.distros.find(profile.distro)
if distro is None:
runtime.set_error("orphan_system2")
raise "error"
f1 = self.get_pxelinux_filename(system.name)
- f2 = os.path.join(config.config().tftpboot, "pxelinux.cfg", f1)
- f3 = os.path.join(config.config().tftpboot, "systems", f1)
+ f2 = os.path.join(self.settings.tftpboot, "pxelinux.cfg", f1)
+ f3 = os.path.join(self.settings.tftpboot, "systems", f1)
self.write_pxelinux_file(f2,system,profile,distro)
self.write_system_file(f3,system)
@@ -228,7 +229,7 @@ class BootSync:
self.tee(fd,"label linux\n")
self.tee(fd," kernel %s\n" % kernel_path)
kopts = self.blend_kernel_options((
- config.config().kernel_options,
+ self.settings.kernel_options,
profile.kernel_options,
distro.kernel_options,
system.kernel_options
@@ -238,7 +239,7 @@ class BootSync:
# if kickstart path is local, we've already copied it into
# the HTTP mirror, so make it something anaconda can get at
if kickstart_path.startswith("/"):
- kickstart_path = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (config.config().server, profile.name)
+ kickstart_path = "http://%s/cobbler/kickstarts/%s/ks.cfg" % self.settings.server, profile.name)
nextline = nextline + " ks=%s" % kickstart_path
self.tee(fd, nextline)
self.close_file(fd)
@@ -265,7 +266,7 @@ class BootSync:
# if kickstart path is local, we've already copied it into
# the HTTP mirror, so make it something anaconda can get at
if profile.kickstart and profile.kickstart.startswith("/"):
- profile.kickstart = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (config.config().server, profile.name)
+ profile.kickstart = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (self.settings.server, profile.name)
self.tee(fd,syck.dump(profile.to_datastruct()))
self.close_file(fd)
@@ -385,8 +386,3 @@ class BootSync:
# end result is a new fragment of a kernel options string
return " ".join(results)
-__instance = BootSync()
-
-def bootsync()
- return __instance
-
diff --git a/cobbler/systems.py b/cobbler/systems.py
index 47dc042..9c48c00 100644
--- a/cobbler/systems.py
+++ b/cobbler/systems.py
@@ -9,6 +9,7 @@ they belong to.
"""
class Systems(Collection):
_item_factory = system.System
+ _filename = "/var/lib/cobbler/systems"
def remove(self,name):
"""
diff --git a/cobbler/util.py b/cobbler/util.py
index ed18869..a851440 100644
--- a/cobbler/util.py
+++ b/cobbler/util.py
@@ -9,10 +9,18 @@ import re
import socket
import glob
import weakref
+import subprocess
re_kernel = re.compile(r'vmlinuz-(\d+)\.(\d+)\.(\d+)-(.*)')
re_initrd = re.compile(r'initrd-(\d+)\.(\d+)\.(\d+)-(.*).img')
+def get_host_ip(ip):
+ handle = subprocess.Popen("/usr/bin/gethostip %s" % ip,
+ shell=True
+ stdout=PIPE)
+ out = handle.stdout
+ results = out.read()
+ return results.split(" ")[-1]
def find_system_identifier(self,strdata):
"""
diff --git a/cobbler/~api.py b/cobbler/~api.py
deleted file mode 100644
index e69de29..0000000
--- a/cobbler/~api.py
+++ /dev/null
diff --git a/tests/tests.py b/tests/tests.py
index 34b66d8..1db0ba3 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -57,21 +57,21 @@ class BootTest(unittest.TestCase):
self.assertTrue(distro.set_name("testdistro0"))
self.assertTrue(distro.set_kernel(self.fk_kernel))
self.assertTrue(distro.set_initrd(self.fk_initrd))
- self.assertTrue(self.api.get_distros().add(distro))
- self.assertTrue(self.api.get_distros().find("testdistro0"))
+ self.assertTrue(self.api.distros().add(distro))
+ self.assertTrue(self.api.distros().find("testdistro0"))
profile = self.api.new_profile()
self.assertTrue(profile.set_name("testprofile0"))
self.assertTrue(profile.set_distro("testdistro0"))
self.assertTrue(profile.set_kickstart(FAKE_KICKSTART))
- self.assertTrue(self.api.get_profiles().add(profile))
- self.assertTrue(self.api.get_profiles().find("testprofile0"))
+ self.assertTrue(self.api.profiles().add(profile))
+ self.assertTrue(self.api.profiles().find("testprofile0"))
system = self.api.new_system()
self.assertTrue(system.set_name(self.hostname))
self.assertTrue(system.set_profile("testprofile0"))
- self.assertTrue(self.api.get_systems().add(system))
- self.assertTrue(self.api.get_systems().find(self.hostname))
+ self.assertTrue(self.api.systems().add(system))
+ self.assertTrue(self.api.systems().find(self.hostname))
class Utilities(BootTest):
@@ -122,24 +122,24 @@ class Additions(BootTest):
self.assertTrue(distro.set_name("testdistro2"))
self.assertFalse(distro.set_kernel("filedoesntexist"))
self.assertTrue(distro.set_initrd(self.fk_initrd))
- self.assertFalse(self.api.get_distros().add(distro))
- self.assertFalse(self.api.get_distros().find("testdistro2"))
+ self.assertFalse(self.api.distros().add(distro))
+ self.assertFalse(self.api.distros().find("testdistro2"))
def test_invalid_distro_non_referenced_initrd(self):
distro = self.api.new_distro()
self.assertTrue(distro.set_name("testdistro3"))
self.assertTrue(distro.set_kernel(self.fk_kernel))
self.assertFalse(distro.set_initrd("filedoesntexist"))
- self.assertFalse(self.api.get_distros().add(distro))
- self.assertFalse(self.api.get_distros().find("testdistro3"))
+ self.assertFalse(self.api.distros().add(distro))
+ self.assertFalse(self.api.distros().find("testdistro3"))
def test_invalid_profile_non_referenced_distro(self):
profile = self.api.new_profile()
self.assertTrue(profile.set_name("testprofile11"))
self.assertFalse(profile.set_distro("distrodoesntexist"))
self.assertTrue(profile.set_kickstart(FAKE_KICKSTART))
- self.assertFalse(self.api.get_profiles().add(profile))
- self.assertFalse(self.api.get_profiles().find("testprofile2"))
+ self.assertFalse(self.api.profiles().add(profile))
+ self.assertFalse(self.api.profiles().find("testprofile2"))
def test_invalid_profile_kickstart_not_url(self):
profile = self.api.new_profile()
@@ -147,8 +147,8 @@ class Additions(BootTest):
self.assertTrue(profile.set_distro("testdistro0"))
self.assertFalse(profile.set_kickstart("kickstartdoesntexist"))
# since kickstarts are optional, you can still add it
- self.assertTrue(self.api.get_profiles().add(profile))
- self.assertTrue(self.api.get_profiles().find("testprofile12"))
+ self.assertTrue(self.api.profiles().add(profile))
+ self.assertTrue(self.api.profiles().find("testprofile12"))
# now verify the other kickstart forms would still work
self.assertTrue(profile.set_kickstart("http://bar"))
self.assertTrue(profile.set_kickstart("ftp://bar"))
@@ -180,67 +180,67 @@ class Additions(BootTest):
self.assertFalse(profile.set_xen_paravirt(11))
# each item should be 'true' now, so we can add it
# since the failed items don't affect status
- self.assertTrue(self.api.get_profiles().add(profile))
+ self.assertTrue(self.api.profiles().add(profile))
def test_invalid_system_bad_name_host(self):
system = self.api.new_system()
name = "hostnamewontresolveanyway"
self.assertFalse(system.set_name(name))
self.assertTrue(system.set_profile("testprofile0"))
- self.assertFalse(self.api.get_systems().add(system))
- self.assertFalse(self.api.get_systems().find(name))
+ self.assertFalse(self.api.systems().add(system))
+ self.assertFalse(self.api.systems().find(name))
def test_system_name_is_a_MAC(self):
system = self.api.new_system()
name = "00:16:41:14:B7:71"
self.assertTrue(system.set_name(name))
self.assertTrue(system.set_profile("testprofile0"))
- self.assertTrue(self.api.get_systems().add(system))
- self.assertTrue(self.api.get_systems().find(name))
+ self.assertTrue(self.api.systems().add(system))
+ self.assertTrue(self.api.systems().find(name))
def test_system_name_is_an_IP(self):
system = self.api.new_system()
name = "192.168.1.54"
self.assertTrue(system.set_name(name))
self.assertTrue(system.set_profile("testprofile0"))
- self.assertTrue(self.api.get_systems().add(system))
- self.assertTrue(self.api.get_systems().find(name))
+ self.assertTrue(self.api.systems().add(system))
+ self.assertTrue(self.api.systems().find(name))
def test_invalid_system_non_referenced_profile(self):
system = self.api.new_system()
self.assertTrue(system.set_name(self.hostname))
self.assertFalse(system.set_profile("profiledoesntexist"))
- self.assertFalse(self.api.get_systems().add(system))
+ self.assertFalse(self.api.systems().add(system))
class Deletions(BootTest):
def test_invalid_delete_profile_doesnt_exist(self):
- self.assertFalse(self.api.get_profiles().remove("doesnotexist"))
+ self.assertFalse(self.api.profiles().remove("doesnotexist"))
def test_invalid_delete_profile_would_orphan_systems(self):
self.make_basic_config()
- self.assertFalse(self.api.get_profiles().remove("testprofile0"))
+ self.assertFalse(self.api.profiles().remove("testprofile0"))
def test_invalid_delete_system_doesnt_exist(self):
- self.assertFalse(self.api.get_systems().remove("doesnotexist"))
+ self.assertFalse(self.api.systems().remove("doesnotexist"))
def test_invalid_delete_distro_doesnt_exist(self):
- self.assertFalse(self.api.get_distros().remove("doesnotexist"))
+ self.assertFalse(self.api.distros().remove("doesnotexist"))
def test_invalid_delete_distro_would_orphan_profile(self):
self.make_basic_config()
- self.assertFalse(self.api.get_distros().remove("testdistro0"))
+ self.assertFalse(self.api.distros().remove("testdistro0"))
def test_working_deletes(self):
self.api.clear()
self.make_basic_config()
- self.assertTrue(self.api.get_systems().remove(self.hostname))
+ self.assertTrue(self.api.systems().remove(self.hostname))
self.api.serialize()
- self.assertTrue(self.api.get_profiles().remove("testprofile0"))
- self.assertTrue(self.api.get_distros().remove("testdistro0"))
- self.assertFalse(self.api.get_systems().find(self.hostname))
- self.assertFalse(self.api.get_profiles().find("testprofile0"))
- self.assertFalse(self.api.get_distros().find("testdistro0"))
+ self.assertTrue(self.api.profiles().remove("testprofile0"))
+ self.assertTrue(self.api.distros().remove("testdistro0"))
+ self.assertFalse(self.api.systems().find(self.hostname))
+ self.assertFalse(self.api.profiles().find("testprofile0"))
+ self.assertFalse(self.api.distros().find("testdistro0"))
class TestCheck(BootTest):
@@ -271,9 +271,9 @@ class TestListings(BootTest):
# check to see if the collection listings output something.
# this is a minimal check, mainly for coverage, not validity
self.make_basic_config()
- self.assertTrue(len(self.api.get_systems().printable()) > 0)
- self.assertTrue(len(self.api.get_profiles().printable()) > 0)
- self.assertTrue(len(self.api.get_distros().printable()) > 0)
+ self.assertTrue(len(self.api.systems().printable()) > 0)
+ self.assertTrue(len(self.api.profiles().printable()) > 0)
+ self.assertTrue(len(self.api.distros().printable()) > 0)
class TestCLIBasic(BootTest):