summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile22
-rw-r--r--TODO4
-rw-r--r--bootconf.man217
-rw-r--r--cobbler.pod4
-rw-r--r--cobbler/IPy.py120
-rw-r--r--cobbler/api.py81
-rw-r--r--cobbler/check.py18
-rwxr-xr-xcobbler/cobbler6
-rwxr-xr-xcobbler/cobbler.py32
-rw-r--r--cobbler/config.py42
-rw-r--r--cobbler/msg.py8
-rw-r--r--cobbler/sync.py44
-rw-r--r--cobbler/util.py24
-rw-r--r--examples/example_ks4
-rw-r--r--tests/tests.py40
15 files changed, 221 insertions, 445 deletions
diff --git a/Makefile b/Makefile
index d8174f8..8e917c0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,19 +1,21 @@
-all: clean test manpage install
-
clean:
- @rm -f *.gz *.rpm MANIFEST
- @rm -rf cobbler-* dist build
+ \rm -f cobbler*.gz cobbler*.rpm MANIFEST
+ \rm -rf cobbler-* dist build
manpage:
- pod2man --center="cobbler" --release="" cobbler.pod > cobbler.1
- @\rm -f cobbler.1.gz
- gzip cobbler.1
+ pod2man --center="cobbler" --release="" cobbler.pod | gzip -c > cobbler.1.gz
test:
python tests/tests.py
-install: clean
+install: clean manpage
python setup.py sdist
cp dist/*.gz .
- rpmbuild --define "_topdir %(pwd)" --define "_builddir %{_topdir}" --define "_rpmdir %{_topdir}" --define "_srcrpmdir %{_topdir}" --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' --define "_specdir %{_topdir}" --define "_sourcedir %{_topdir}" -ba cobbler.spec
-
+ rpmbuild --define "_topdir %(pwd)" \
+ --define "_builddir %{_topdir}" \
+ --define "_rpmdir %{_topdir}" \
+ --define "_srcrpmdir %{_topdir}" \
+ --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \
+ --define "_specdir %{_topdir}" \
+ --define "_sourcedir %{_topdir}" \
+ -ba cobbler.spec
diff --git a/TODO b/TODO
index 8c18700..b333c8d 100644
--- a/TODO
+++ b/TODO
@@ -7,13 +7,13 @@ I - Manpage (in progress) .. continuously...
distro add [--kickstart] optionally as alternative to
[--kernel=] and [--initrd=]. If so, groups can inherit kickstart info
from the group (can and must if the distro has a kickstart specified).
- - Don't require root, check for permissions on dirpaths we touch.
+ - Don't require root, check for permissions on dirpaths we touch.
- Support pxelinux's default directory
- Subnet creation shorthand ... (system objects that match multiples?)
- MAC ranges for Xen (how best to do this? shelve this for now).
- If there is a parse error in /etc/cobbler.conf and the user
deletes that file, fix the bug that also deletes the file in /var.
-
+
LEGEND
T = DONE. SOME TESTING STILL TBA.
diff --git a/bootconf.man b/bootconf.man
deleted file mode 100644
index 7073b62..0000000
--- a/bootconf.man
+++ /dev/null
@@ -1,217 +0,0 @@
-.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
-.\"
-.\" Standard preamble:
-.\" ========================================================================
-.de Sh \" Subsection heading
-.br
-.if t .Sp
-.ne 5
-.PP
-\fB\\$1\fR
-.PP
-..
-.de Sp \" Vertical space (when we can't use .PP)
-.if t .sp .5v
-.if n .sp
-..
-.de Vb \" Begin verbatim text
-.ft CW
-.nf
-.ne \\$1
-..
-.de Ve \" End verbatim text
-.ft R
-.fi
-..
-.\" Set up some character translations and predefined strings. \*(-- will
-.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
-.\" double quote, and \*(R" will give a right double quote. | will give a
-.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to
-.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C'
-.\" expand to `' in nroff, nothing in troff, for use with C<>.
-.tr \(*W-|\(bv\*(Tr
-.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
-.ie n \{\
-. ds -- \(*W-
-. ds PI pi
-. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
-. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
-. ds L" ""
-. ds R" ""
-. ds C` ""
-. ds C' ""
-'br\}
-.el\{\
-. ds -- \|\(em\|
-. ds PI \(*p
-. ds L" ``
-. ds R" ''
-'br\}
-.\"
-.\" If the F register is turned on, we'll generate index entries on stderr for
-.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
-.\" entries marked with X<> in POD. Of course, you'll have to process the
-.\" output yourself in some meaningful fashion.
-.if \nF \{\
-. de IX
-. tm Index:\\$1\t\\n%\t"\\$2"
-..
-. nr % 0
-. rr F
-.\}
-.\"
-.\" For nroff, turn off justification. Always turn off hyphenation; it makes
-.\" way too many mistakes in technical documents.
-.hy 0
-.if n .na
-.\"
-.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
-.\" Fear. Run. Save yourself. No user-serviceable parts.
-. \" fudge factors for nroff and troff
-.if n \{\
-. ds #H 0
-. ds #V .8m
-. ds #F .3m
-. ds #[ \f1
-. ds #] \fP
-.\}
-.if t \{\
-. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
-. ds #V .6m
-. ds #F 0
-. ds #[ \&
-. ds #] \&
-.\}
-. \" simple accents for nroff and troff
-.if n \{\
-. ds ' \&
-. ds ` \&
-. ds ^ \&
-. ds , \&
-. ds ~ ~
-. ds /
-.\}
-.if t \{\
-. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
-. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
-. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
-. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
-. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
-. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
-.\}
-. \" troff and (daisy-wheel) nroff accents
-.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
-.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
-.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
-.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
-.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
-.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
-.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
-.ds ae a\h'-(\w'a'u*4/10)'e
-.ds Ae A\h'-(\w'A'u*4/10)'E
-. \" corrections for vroff
-.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
-.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
-. \" for low resolution devices (crt and lpr)
-.if \n(.H>23 .if \n(.V>19 \
-\{\
-. ds : e
-. ds 8 ss
-. ds o a
-. ds d- d\h'-1'\(ga
-. ds D- D\h'-1'\(hy
-. ds th \o'bp'
-. ds Th \o'LP'
-. ds ae ae
-. ds Ae AE
-.\}
-.rm #[ #] #H #V #F C
-.\" ========================================================================
-.\"
-.IX Title "BOOTCONF 1"
-.TH BOOTCONF 1 "2006-04-05" "perl v5.8.8" "User Contributed Perl Documentation"
-bootconf \- simple configuration of \s-1PXE\s0 boot + kickstart environments
-.SH "SYNOPSIS"
-.IX Header "SYNOPSIS"
-bootconf command [subcommand] [\-\-arg1=] [\-\-arg2=]
-See '\s-1INSTRUCTIONS\s0'.
-.SH "DESCRIPTION"
-.IX Header "DESCRIPTION"
-Configuration of a boot server involves a lot of things. Perhaps too many. These items include a tftpd server, a dhcpd server, syslinux, and a lot of copying files around and creating other config files. Bootconf makes these things simpler.
-.PP
-bootconf requires root access.
-.SH "INSTRUCTIONS"
-.IX Header "INSTRUCTIONS"
-\&\fBbefore you start...\fR
-.PP
-First install dhcpd, tftpd, and syslinux.
-You'll also need \s-1FTP\s0, \s-1HTTP\s0, or \s-1NFS\s0 to serve kickstarts (if you want them)
-And you'll also have to edit dhcpd.conf for your particular \s-1DHCP\s0 environment.
-Yes, \s-1DHCP\s0 is a required piece.
-.PP
-.Vb 3
-\& yum install dhcp tftp-server syslinux
-\& ...
-\& vi /etc/dhcpd.conf
-.Ve
-.PP
-\&\fBbootconf check\fR
-.PP
-This verifies that everything you need to set up is set up.
-This will mention any missing services and obvious configuration errors.
-Errors? Correct any problems reported, then run check until there are none.
-.PP
-\&\fBbootconf distro add \-\-name=distro_name \-\-kernel=path \-\-initrd=path\fR
-.PP
-Defines a distribution as a matched pair of a kernel and an initrd, and
-gives it a name. This could be 'fc5\-i386' or 'foosball\-73', it's your name.
-.PP
-\&\fBbootconf group add \-\-name=group_name \-\-kickstart=path \-\-distro=distro_name\fR
-.PP
-Defines a provisioning group, which is a distro (you did define a distro, right?) and a kickstart. Kickstarts are served up by URLs, though this needs to be a file. The distro parameter is whatever named you used earlier.
-.PP
-\&\fBbootconf system add \-\-name=ip|mac|hostname \-\-group=group_name\fR
-.PP
-Defines a system. A system can't have a free form name, it has to be a hostname that resolves to an \s-1IP\s0, a \s-1MAC\s0 address, or an actual \s-1IP\s0. The group parameter is the group name you used with \*(L"group add\*(R".
-.PP
-\&\fBbootconf distro list\fR
-.PP
-Gives a list of distributions that are currently configured. If you specified any kernels or initrd's by directories instead of full paths, it will indicate which ones are currently picked.
-.PP
-\&\fBbootconf group list\fR
-.PP
-Gives a list of groups that are currently configured.
-.PP
-\&\fBbootconf system list\fR
-.PP
-Gives a list of the sytems that are currently configured.
-.PP
-\&\fBbootconf distro remove \-\-name=distro_name\fR
-.PP
-Deletes a distro from the stored configuration. You can't delete a distro if any groups reference that distro, you'd have to delete all of the systems using that distro first. Deleting a distro just means that you no longer want it configured for booting, it does not delete any files.
-.PP
-\&\fBbootconf group remove \-\-name=group_name\fR
-.PP
-Deletes a group from the stored configuration. You can't delete a group if any systems reference it. Does not actually delete any kickstart files.
-.PP
-\&\fBbootconf system remove \-\-name=system_name\fR
-.PP
-Deletes a system from the stored configuration. You can always delete a system.
-.PP
-\&\fBbootconf sync [\-\-dryrun]\fR
-.PP
-Applies the stored configuration to the system. If you have any configuration errors, bootconf sync will ask you to run 'bootconf check' and fix them first.
-.PP
-Usage of the dryrun option will show you the pending changes without actually making them.
-.SH "CONFIGURATION_FILES"
-.IX Header "CONFIGURATION_FILES"
-bootconf uses /etc/bootconf.conf to store it's internal state. You may want to manually edit the global options at the top of the file for some reason. If you do so, run 'bootconf check' again to ensure there are no errors introduced from this. In a worse case scenario, you can delete the file and the next run of bootconf will recreate it.
-.SH "BUGS"
-.IX Header "BUGS"
-It's a new product, there could be one. Somewhere.
-.SH "EXIT_STATUS"
-.IX Header "EXIT_STATUS"
-bootconf returns a zero for success and non-zero for failure.
-.SH "AUTHOR"
-.IX Header "AUTHOR"
-Michael DeHaan <mdehaan@redhat.com>
diff --git a/cobbler.pod b/cobbler.pod
index 12b8f29..9d7ba72 100644
--- a/cobbler.pod
+++ b/cobbler.pod
@@ -34,7 +34,7 @@ B<cobbler distro add --name=<string> --kernel=<path> --initrd=<path> [--kopts=<s
Defines a distribution. Minimally, this is a matched set of an initrd and a kernel that has a name, such as 'fc5-i386' or 'rhel4-x86_64'. Here you can also override and extend the default kernel options ('kopts') specified in /etc/cobbler.conf (kernel options are a comma seperated list of key=value pairs).
-B<cobbler profile add --name=group_name --distro=<name> [--kickstart=<url>] [--kopts=<string>] [--xen-name=<string>] [--xen-file-size=<gigabytes>] [--xen-ram=<megabytes>]
+B<cobbler profile add --name=group_name --distro=<name> [--kickstart=<url>] [--kopts=<string>] [--xen-name=<string>] [--xen-file-size=<gigabytes>] [--xen-ram=<megabytes>]
Defines a provisioning profile, which is a distro, some other optional parameters, and a name for the profile. Almost always you will want to specify a kickstart file. Kickstarts can be nfs://, http://, or ftp:// -- or an absolute path. For file paths, cobbler will copy the kickstarts and serve them up as http.
@@ -70,7 +70,7 @@ Configurations are saved in /var/cobbler/cobbler.conf and are not applied until
=head1 PXE EXAMPLE
-It is easier to understand cobbler once having gone through some examples.
+It is easier to understand cobbler once having gone through some examples.
B<cobbler check>
diff --git a/cobbler/IPy.py b/cobbler/IPy.py
index ac2b1c5..6f6af41 100644
--- a/cobbler/IPy.py
+++ b/cobbler/IPy.py
@@ -94,7 +94,7 @@ WantPrefixLen
>>> ip.WantPrefixLen = 3
>>> print ip
10.0.0.0-10.0.0.0
-
+
Further Information might be available at http://c0re.jp/c0de/IPy/
@@ -108,7 +108,7 @@ TODO:
* 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
+ * update address type tables
* first-last notation should be allowed for IPv6
* add IPv6 docstring examples
* check better for negative parameters
@@ -154,7 +154,7 @@ IPv6ranges = {
'00000001' : 'UNASSIGNED', # 100::/8
'0000001' : 'NSAP', # 200::/7
'0000010' : 'IPX', # 400::/7
- '0000011' : 'UNASSIGNED', # 600::/7
+ '0000011' : 'UNASSIGNED', # 600::/7
'00001' : 'UNASSIGNED', # 800::/5
'0001' : 'UNASSIGNED', # 1000::/4
'0010000000000000' : 'RESERVED', # 2000::/16 Reserved
@@ -166,7 +166,7 @@ IPv6ranges = {
'0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056]
'0011111111111110' : '6BONE', # 3FFE::/16 6bone Testing [RFC2471]
'0011111111111111' : 'RESERVED', # 3FFF::/16 Reserved
- '010' : 'GLOBAL-UNICAST', # 4000::/3
+ '010' : 'GLOBAL-UNICAST', # 4000::/3
'011' : 'UNASSIGNED', # 6000::/3
'100' : 'GEO-UNICAST', # 8000::/3
'101' : 'UNASSIGNED', # A000::/3
@@ -191,10 +191,10 @@ class IPint:
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
@@ -218,10 +218,10 @@ class IPint:
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)
@@ -260,7 +260,7 @@ class IPint:
raise ValueError, "last address should be larger than first"
size = last - self.ip
netbits = _count1Bits(size)
- elif len(x) == 1:
+ elif len(x) == 1:
x = data.split('/')
# if no prefix is given use defaults
if len(x) == 1:
@@ -276,7 +276,7 @@ class IPint:
(netmask, vers) = parseAddress(prefixlen)
if vers != 4:
raise ValueError, "netmask must be IPv4"
- prefixlen = _netmaskToPrefixlen(netmask)
+ prefixlen = _netmaskToPrefixlen(netmask)
elif len(x) > 2:
raise ValueError, "only one '-' allowed in IP Address"
else:
@@ -296,8 +296,8 @@ class IPint:
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)
-
+ 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.
@@ -351,7 +351,7 @@ class IPint:
"""
if (self._ipversion == 4 and self._prefixlen == 32) or \
- (self._ipversion == 6 and self._prefixlen == 128):
+ (self._ipversion == 6 and self._prefixlen == 128):
if self.NoPrefixForSingleIp:
want = 0
if want == None:
@@ -380,7 +380,7 @@ class IPint:
# strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA
# strDec 2130706433 42540616829182469433547974687817795834
- def strBin(self, wantprefixlen = None):
+ def strBin(self, wantprefixlen = None):
"""Return a string representation as a binary value.
>>> print IP('127.0.0.1').strBin()
@@ -389,7 +389,7 @@ class IPint:
if self._ipversion == 4:
- bits = 32
+ bits = 32
elif self._ipversion == 6:
bits = 128
else:
@@ -408,10 +408,10 @@ class IPint:
>>> 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:
@@ -450,16 +450,16 @@ class IPint:
if self.WantPrefixLen == None and wantprefixlen == None:
wantprefixlen = 1
-
+
if self._ipversion == 4:
- ret = self.strFullsize(0)
+ 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):
@@ -473,7 +473,7 @@ class IPint:
if self.WantPrefixLen == None and wantprefixlen == None:
wantprefixlen = 1
-
+
return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
def strHex(self, wantprefixlen = None):
@@ -530,9 +530,9 @@ class IPint:
# this could be greatly improved
if self._ipversion == 4:
- iprange = IPv4ranges
+ iprange = IPv4ranges
elif self._ipversion == 6:
- iprange = IPv6ranges
+ iprange = IPv6ranges
else:
raise ValueError, "only IPv4 and IPv6 supported"
@@ -596,7 +596,7 @@ class IPint:
else:
raise ValueError, "only IPv4 and IPv6 supported"
- return 2L ** locallen
+ return 2L ** locallen
def __len__(self):
@@ -607,13 +607,13 @@ class IPint:
# 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())
@@ -637,7 +637,7 @@ class IPint:
return self.ip + long(key)
-
+
def __contains__(self, item):
"""Called to implement membership test operators.
@@ -686,7 +686,7 @@ class IPint:
else:
return 0
-
+
def __str__(self):
"""Dispatch to the prefered String Representation.
@@ -706,7 +706,7 @@ class IPint:
IP('10.0.0.0/24')
"""
- return("IPint('%s')" % (self.strCompressed(1)))
+ return("IPint('%s')" % (self.strCompressed(1)))
def __cmp__(self, other):
@@ -735,11 +735,11 @@ class IPint:
# Im not really sure if this is "the right thing to do"
if self._prefixlen < other.prefixlen():
- return (other.prefixlen() - self._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
@@ -752,17 +752,17 @@ class IPint:
# 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
+ 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
@@ -834,7 +834,7 @@ class IP(IPint):
['128.in-addr.arpa.']
>>> IP('128.0.0.0/7').reverseNames()
['128.in-addr.arpa.', '129.in-addr.arpa.']
-
+
"""
if self._ipversion == 4:
@@ -866,8 +866,8 @@ class IP(IPint):
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.
@@ -886,7 +886,7 @@ class IP(IPint):
s = self.strFullsize(0)
s = s.split('.')
s.reverse()
- first_byte_index = int(4 - (self._prefixlen / 8))
+ 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':
@@ -919,7 +919,7 @@ class IP(IPint):
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)
@@ -942,7 +942,7 @@ class IP(IPint):
IP('10.0.0.0/8')
"""
- return("IP('%s')" % (self.strCompressed(1)))
+ return("IP('%s')" % (self.strCompressed(1)))
def __add__(self, other):
"""Emulate numeric objects through network aggregation"""
@@ -957,7 +957,7 @@ class IP(IPint):
return other.__add__(self)
else:
ret = IP(self.int())
- ret._prefixlen = self.prefixlen() - 1
+ ret._prefixlen = self.prefixlen() - 1
return ret
def parseAddress(ipstr):
@@ -986,7 +986,7 @@ def parseAddress(ipstr):
return (ret, 4)
else:
return (ret, 6)
-
+
if ipstr.find(':') != -1:
# assume IPv6
if ipstr.find(':::') != -1:
@@ -1010,7 +1010,7 @@ def parseAddress(ipstr):
# 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('')
@@ -1022,7 +1022,7 @@ def parseAddress(ipstr):
for x in hextets:
if len(x) < 4:
x = ((4 - len(x)) * '0') + x
- if int(x, 16) < 0 or int(x, 16) > 0xffff:
+ 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)
@@ -1030,7 +1030,7 @@ def parseAddress(ipstr):
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('.')
@@ -1064,9 +1064,9 @@ def intToIp(ip, version):
if ip < 0:
raise ValueError, "IPs can't be negative: %d" % (ip)
-
+
ret = ''
- if version == 4:
+ if version == 4:
if ip > 0xffffffffL:
raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip))
for l in range(4):
@@ -1084,7 +1084,7 @@ def intToIp(ip, version):
ret = ret[1:]
else:
raise ValueError, "only IPv4 and IPv6 supported"
-
+
return ret;
def _ipVersionToLen(version):
@@ -1124,7 +1124,7 @@ _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."""
@@ -1142,7 +1142,7 @@ def _intToBin(val):
# remove leading zeros
while ret[0] == '0' and len(ret) > 1:
ret = ret[1:]
- return ret
+ return ret
def _count1Bits(num):
"""Find the highest bit set to 1 in an integer."""
@@ -1165,12 +1165,12 @@ def _count0Bits(num):
break
num = num >> 1
ret += 1
- return ret
+ 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.
@@ -1186,11 +1186,11 @@ def _checkPrefix(ip, prefixlen, version):
# TODO: unify this v4/v6/invalid code in a function
bits = _ipVersionToLen(version)
-
+
if prefixlen < 0 or prefixlen > bits:
return None
- if ip == 0:
+ if ip == 0:
zbits = bits + 1
else:
zbits = _count0Bits(ip)
@@ -1205,7 +1205,7 @@ def _checkNetmask(netmask, masklen):
num = long(netmask)
bits = masklen
-
+
# remove zero bits at the end
while (num & 1) == 0:
num = num >> 1
@@ -1226,7 +1226,7 @@ def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
return 1
else:
return 0
-
+
def _netmaskToPrefixlen(netmask):
"""Convert an Integer reprsenting a Netmask to an prefixlen.
@@ -1251,7 +1251,7 @@ def _prefixlenToNetmask(prefixlen, version):
elif prefixlen < 0:
raise ValueError, "Prefixlen must be > 0"
return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
-
+
def _test():
import doctest, IPy
diff --git a/cobbler/api.py b/cobbler/api.py
index 953dd2e..f2a18f8 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -1,5 +1,5 @@
"""
-python API module for BootConf
+python API module for BootConf
see source for bootconf.py for a good API reference
Michael DeHaan <mdehaan@redhat.com>
@@ -36,7 +36,6 @@ class BootAPI:
self.config.serialize()
except:
traceback.print_exc()
- pass
if not self.config.files_exist():
self.config.serialize()
@@ -57,7 +56,7 @@ class BootAPI:
def get_profiles(self):
"""
- Return the current list of profiles
+ Return the current list of profiles
"""
return self.config.get_profiles()
@@ -105,10 +104,10 @@ class BootAPI:
def sync(self,dry_run=True):
"""
Take the values currently written to the configuration files in
- /etc, and /var, and build out the information tree found in
+ /etc, and /var, and build out the information tree found in
/tftpboot. Any operations done in the API that have not been
saved with serialize() will NOT be synchronized with this command.
- """
+ """
self.config.deserialize();
configurator = sync.BootSync(self)
return configurator.sync(dry_run)
@@ -118,8 +117,8 @@ class BootAPI:
"""
Save the config file(s) to disk.
"""
- self.config.serialize()
-
+ self.config.serialize()
+
def deserialize(self):
"""
Load the current configuration from config file(s)
@@ -150,8 +149,8 @@ class Collection:
for feeding to a serializer (such as YAML)
"""
return [x.to_datastruct() for x in self.listing.values()]
-
-
+
+
def add(self,ref):
"""
Add an object to the collection, if it's valid. Returns True
@@ -159,7 +158,7 @@ class Collection:
object specified by ref deems itself invalid (and therefore
won't be added to the collection).
"""
- if ref is None or not ref.is_valid():
+ if ref is None or not ref.is_valid():
if self.api.last_error is None or self.api.last_error == "":
self.api.last_error = m("bad_param")
return False
@@ -175,7 +174,7 @@ class Collection:
"""
buf = ""
values = map(lambda(a): a.printable(), sorted(self.listing.values()))
- if len(values) > 0:
+ if len(values) > 0:
return "\n\n".join(values)
else:
return m("empty_list")
@@ -194,13 +193,13 @@ class Collection:
"""
for a in self.listing.values():
yield a
-
+
def __len__(self):
"""
Returns size of the collection
"""
return len(self.listing.values())
-
+
#--------------------------------------------
@@ -219,7 +218,7 @@ class Distros(Collection):
self.api = api
self.listing = {}
if seed_data is not None:
- for x in seed_data:
+ for x in seed_data:
self.add(Distro(self.api,x))
def remove(self,name):
@@ -236,7 +235,7 @@ class Distros(Collection):
return True
self.api.last_error = m("delete_nothing")
return False
-
+
#--------------------------------------------
@@ -252,7 +251,7 @@ class Profiles(Collection):
self.api = api
self.listing = {}
if seed_data is not None:
- for x in seed_data:
+ for x in seed_data:
self.add(Profile(self.api,x))
def remove(self,name):
@@ -268,7 +267,7 @@ class Profiles(Collection):
return True
self.api.last_error = m("delete_nothing")
return False
-
+
#--------------------------------------------
@@ -282,7 +281,7 @@ class Systems(Collection):
self.api = api
self.listing = {}
if seed_data is not None:
- for x in seed_data:
+ for x in seed_data:
self.add(System(self.api,x))
def remove(self,name):
@@ -294,7 +293,7 @@ class Systems(Collection):
return True
self.api.last_error = m("delete_nothing")
return False
-
+
#-----------------------------------------
@@ -302,7 +301,7 @@ class Systems(Collection):
An Item is a serializable thing that can appear in a Collection
"""
class Item:
-
+
def set_name(self,name):
"""
@@ -326,7 +325,7 @@ class Item:
i.e. dictionaries/arrays/scalars.
"""
raise exceptions.NotImplementedError
-
+
def is_valid(self):
"""
The individual set_ methods will return failure if any set is
@@ -334,7 +333,7 @@ class Item:
the object is well formed ... i.e. have all of the important
items been set, are they free of conflicts, etc.
"""
- return False
+ return False
#------------------------------------------
@@ -355,7 +354,7 @@ class Distro(Item):
def set_kernel(self,kernel):
"""
Specifies a kernel. The kernel parameter is a full path, a filename
- in the configured kernel directory (set in /etc/cobbler.conf) or a
+ in the configured kernel directory (set in /etc/cobbler.conf) or a
directory path that would contain a selectable kernel. Kernel
naming conventions are checked, see docs in the utils module
for find_kernel.
@@ -387,9 +386,9 @@ class Distro(Item):
return True
def to_datastruct(self):
- return {
- 'name': self.name,
- 'kernel': self.kernel,
+ return {
+ 'name': self.name,
+ 'kernel': self.kernel,
'initrd' : self.initrd,
'kernel_options' : self.kernel_options
}
@@ -426,14 +425,14 @@ class Profile(Item):
self.kickstart = None
self.kernel_options = ''
self.xen_name = 'xenguest'
- self.xen_file_size = 5 # GB
+ self.xen_file_size = 5 # GB
self.xen_ram = 2048 # MB
self.xen_mac = ''
self.xen_paravirt = True
if seed_data is not None:
self.name = seed_data['name']
self.distro = seed_data['distro']
- self.kickstart = seed_data['kickstart']
+ self.kickstart = seed_data['kickstart']
self.kernel_options = seed_data['kernel_options']
self.xen_name = seed_data['xen_name']
if not self.xen_name or self.xen_name == '':
@@ -472,7 +471,7 @@ class Profile(Item):
xen-net-install may do conflict resolution, so this is mostly
a hint... To keep the shell happy, the 'str' cannot
contain wildcards or slashes and may be subject to some other
- untainting later.
+ untainting later.
"""
# no slashes or wildcards
for bad in [ '/', '*', '?' ]:
@@ -490,7 +489,7 @@ class Profile(Item):
let it pick a semi-reasonable size. When in doubt, specify the
size you want.
"""
- # num is a non-negative integer (0 means default)
+ # num is a non-negative integer (0 means default)
try:
inum = int(num)
if inum != float(num):
@@ -505,12 +504,12 @@ class Profile(Item):
def set_xen_mac(self,mac):
"""
For Xen only.
- Specifies the mac address (or possibly later, a range) that
+ Specifies the mac address (or possibly later, a range) that
xen-net-install should try to set on the domU. Seeing these
have a good chance of conflicting with other domU's, especially
on a network, this setting is fairly experimental at this time.
It's recommended that it *not* be used until we can get
- decent use cases for how this might work.
+ decent use cases for how this might work.
"""
# mac needs to be in mac format AA:BB:CC:DD:EE:FF or a range
# ranges currently *not* supported, so we'll fail them
@@ -519,7 +518,7 @@ class Profile(Item):
return True
else:
return False
-
+
def set_xen_paravirt(self,truthiness):
"""
For Xen only.
@@ -534,9 +533,9 @@ class Profile(Item):
elif (truthiness == True or truthiness.lower() == 'true'):
self.xen_paravirt = True
else:
- return False
+ return False
except:
- return False
+ return False
return True
def is_valid(self):
@@ -546,12 +545,12 @@ class Profile(Item):
without a kickstart is *usually* not a good idea).
"""
for x in (self.name, self.distro):
- if x is None:
+ if x is None:
return False
return True
def to_datastruct(self):
- return {
+ return {
'name' : self.name,
'distro' : self.distro,
'kickstart' : self.kickstart,
@@ -589,15 +588,15 @@ class System(Item):
self.name = seed_data['name']
self.profile = seed_data['profile']
self.kernel_options = seed_data['kernel_options']
-
+
def set_name(self,name):
"""
- A name can be a resolvable hostname (it instantly resolved and replaced with the IP),
+ A name can be a resolvable hostname (it instantly resolved and replaced with the IP),
any legal ipv4 address, or any legal mac address. ipv6 is not supported yet but _should_ be.
See utils.py
"""
- new_name = self.api.utils.find_system_identifier(name)
+ new_name = self.api.utils.find_system_identifier(name)
if new_name is None or new_name == False:
self.api.last_error = m("bad_sys_name")
return False
@@ -606,7 +605,7 @@ class System(Item):
def set_profile(self,profile_name):
"""
- Set the system to use a certain named profile. The profile
+ Set the system to use a certain named profile. The profile
must have already been loaded into the Profiles collection.
"""
if self.api.get_profiles().find(profile_name):
diff --git a/cobbler/check.py b/cobbler/check.py
index aa5864e..bd4137a 100644
--- a/cobbler/check.py
+++ b/cobbler/check.py
@@ -1,5 +1,5 @@
# Classes for validating whether asystem is configured for network booting
-#
+#
# Michael DeHaan <mdehaan@redhat.com>
import os
@@ -13,11 +13,11 @@ class BootCheck:
def __init__(self, api):
self.api = api
self.config = self.api.config
-
+
def run(self):
"""
- Returns None if there are no errors, otherwise returns a list
+ Returns None if there are no errors, otherwise returns a list
of things to correct prior to running application 'for real'.
(The CLI usage is "cobbler check" before "cobbler sync")
"""
@@ -60,7 +60,7 @@ class BootCheck:
Check if tftpd is installed
"""
if not os.path.exists(self.config.tftpd_bin):
- status.append(m("no_tftpd"))
+ status.append(m("no_tftpd"))
def check_tftpd_dir(self,status):
"""
@@ -68,7 +68,7 @@ class BootCheck:
"""
if not os.path.exists(self.config.tftpboot):
status.append(m("no_dir") % self.config.tftpboot)
-
+
def check_tftpd_conf(self,status):
"""
@@ -88,10 +88,10 @@ class BootCheck:
if line.find("-s %s" % self.config.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.config.tftpboot, self.config.tftpd_conf))
else:
status.append(m("no_exist") % self.config.tftpd_conf)
-
+
def check_dhcpd_conf(self,status):
"""
@@ -103,10 +103,10 @@ class BootCheck:
match_file = False
f = open(self.config.dhcpd_conf)
for line in f.readlines():
- if line.find("next-server") != -1:
+ if line.find("next-server") != -1:
match_next = True
if line.find("filename") != -1:
- match_file = True
+ match_file = True
if not match_next:
status.append(m("no_line") % (self.config.dhcpd_conf, 'next-server ip-address'))
if not match_file:
diff --git a/cobbler/cobbler b/cobbler/cobbler
new file mode 100755
index 0000000..f62e10f
--- /dev/null
+++ b/cobbler/cobbler
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+
+import sys
+import cobbler
+sys.exit(cobbler.main())
+
diff --git a/cobbler/cobbler.py b/cobbler/cobbler.py
index 30baaff..47c8c5a 100755
--- a/cobbler/cobbler.py
+++ b/cobbler/cobbler.py
@@ -21,7 +21,7 @@ class BootCLI:
self.args = args
self.api = api.BootAPI()
self.commands = {}
- self.commands['distro'] = {
+ self.commands['distro'] = {
'add' : self.distro_edit,
'edit' : self.distro_edit,
'delete' : self.distro_remove,
@@ -46,8 +46,8 @@ class BootCLI:
'check' : self.check,
'distros' : self.distro,
'distro' : self.distro,
- 'profiles' : self.profile,
- 'profile' : self.profile,
+ 'profiles' : self.profile,
+ 'profile' : self.profile,
'systems' : self.system,
'system' : self.system,
'sync' : self.sync,
@@ -100,7 +100,7 @@ class BootCLI:
Delete a system: 'cobbler system remove --name=foo'
"""
commands = {
- '--name' : lambda(a): self.api.get_systems().remove(a)
+ '--name' : lambda(a): self.api.get_systems().remove(a)
}
on_ok = lambda: True
return self.apply_args(args,commands,on_ok,True)
@@ -135,8 +135,8 @@ class BootCLI:
sys = self.api.new_system()
commands = {
'--name' : lambda(a) : sys.set_name(a),
- '--profile' : lambda(a) : sys.set_profile(a),
- '--profiles' : lambda(a) : sys.set_profile(a), # alias
+ '--profile' : lambda(a) : sys.set_profile(a),
+ '--profiles' : lambda(a) : sys.set_profile(a), # alias
'--kopts' : lambda(a) : sys.set_kernel_options(a)
}
on_ok = lambda: self.api.get_systems().add(sys)
@@ -157,13 +157,13 @@ class BootCLI:
'--xen-file-size' : lambda(a) : profile.set_xen_file_size(a),
'--xen-ram' : lambda(a) : profile.set_xen_ram(a)
# the following options are most likely not useful for profiles (yet)
- # primarily due to not being implemented in koan.
+ # primarily due to not being implemented in koan.
# '--xen-mac' : lambda(a) : profile.set_xen_mac(a),
# '--xen-paravirt' : lambda(a) : profile.set_xen_paravirt(a),
}
on_ok = lambda: self.api.get_profiles().add(profile)
return self.apply_args(args,commands,on_ok,True)
-
+
def distro_edit(self,args):
"""
@@ -226,7 +226,7 @@ class BootCLI:
return False
if args[0] in commands:
# if the subargument is in the dispatch table, run
- # the selected command routine with the rest of the
+ # the selected command routine with the rest of the
# arguments
rc = commands[args[0]](args[1:])
if not rc:
@@ -241,13 +241,13 @@ class BootCLI:
"""
Sync the config file with the system config: 'cobbler sync [--dryrun]'
"""
- status = None
+ status = None
if args is not None and "--dryrun" in args:
status = self.api.sync(dry_run=True)
else:
status = self.api.sync(dry_run=False)
return status
-
+
def check(self,args):
"""
@@ -261,16 +261,16 @@ class BootCLI:
return True
else:
print m("need_to_fix")
- for i,x in enumerate(status):
+ for i,x in enumerate(status):
print "#%d: %s" % (i,x)
return False
-
+
def distro(self,args):
"""
Handles any of the 'cobbler distro' subcommands
"""
- return self.curry_args(args, self.commands['distro'])
+ return self.curry_args(args, self.commands['distro'])
def profile(self,args):
@@ -292,9 +292,9 @@ def main():
"""
if os.getuid() != 0:
# while it's true that we don't technically need root, we do need
- # permissions on a relatively long list of files that ordinarily
+ # permissions on a relatively long list of files that ordinarily
# only root has access to, and we don't know specifically what
- # files are where (other distributions in play, etc). It's
+ # files are where (other distributions in play, etc). It's
# fairly safe to assume root is required. This might be patched
# later.
print m("need_root")
diff --git a/cobbler/config.py b/cobbler/config.py
index 8c8991a..26192a7 100644
--- a/cobbler/config.py
+++ b/cobbler/config.py
@@ -3,17 +3,19 @@
#
# Michael DeHaan <mdehaan@redhat.com>
-import api
+import api
import util
from msg import *
+import yaml # the yaml parser from RHN's spec-tree (Howell/Evans)
import os
-import yaml # python yaml 3000 from pyyaml.org, soon to be in extras
import traceback
global_settings_file = "/etc/cobbler.conf"
global_state_file = "/var/lib/cobbler/cobbler.conf"
+
+
class BootConfig:
def __init__(self,api):
@@ -52,7 +54,7 @@ class BootConfig:
self.tftpboot = "/tftpboot"
self.dhcpd_conf = "/etc/dhcpd.conf"
self.tftpd_conf = "/etc/xinetd.d/tftp"
- self.pxelinux = "/usr/lib/syslinux/pxelinux.0"
+ 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"
@@ -91,7 +93,7 @@ class BootConfig:
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)
@@ -109,21 +111,20 @@ class BootConfig:
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 = {}
+ 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()
- #print "DEBUG: %s" % (world)
- return world
+ return world
def from_hash(self,hash,is_etc):
@@ -150,7 +151,7 @@ class BootConfig:
settings = None
state = None
-
+
# ------
# dump global config (pathing, urls, etc)...
try:
@@ -160,11 +161,13 @@ class BootConfig:
return False
data = self.to_hash(True)
settings.write(yaml.dump(data))
-
+
# ------
# dump internal state (distros, profiles, systems...)
if not os.path.isdir(os.path.dirname(self.state_file)):
- os.makedirs(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:
@@ -186,28 +189,31 @@ class BootConfig:
# -----
# load global config (pathing, urls, etc)...
try:
- settings = yaml.load(open(self.settings_file,"r").read())
+ settings = yaml.load(open(self.settings_file,"r").read()).next()
if settings is not None:
- return self.from_hash(settings,True)
+ self.from_hash(settings,True)
else:
- print "WARNING: no %s data?" % self.settings_file
+ self.last_error = m("parse_error")
+ return False
except:
+ traceback.print_exc()
self.api.last_error = m("parse_error")
return False
# -----
# load internal state(distros, systems, profiles...)
try:
- state = yaml.load(open(self.state_file,"r").read())
+ state = yaml.load(open(self.state_file,"r").read()).next()
if state is not None:
- return self.from_hash(state,False)
+ self.from_hash(state,False)
else:
- print "WARNING: no %s data?" % self.state_file
+ self.last_error = m("parse_error")
+ return False
except:
traceback.print_exc()
self.api.last_error = m("parse_error2")
return False
-
+
# all good
return True
diff --git a/cobbler/msg.py b/cobbler/msg.py
index e974bc6..d770fd2 100644
--- a/cobbler/msg.py
+++ b/cobbler/msg.py
@@ -22,13 +22,13 @@ msg_table = {
"need_to_fix" : "the following potential problems were detected:",
"need_root" : "cobber must be run as root",
"no_dhcpd" : "can't find dhcpd, try 'yum install dhcpd'",
- "no_pxelinux" : "can't find pxelinux, try 'yum install pxelinux'",
+ "no_pxelinux" : "can't find pxelinux, try 'yum install pxelinux'",
"no_tftpd" : "can't find tftpd, try 'yum install tftpd'",
"no_dir" : "can't find %s, need to create it",
"chg_attrib" : "need to change '%s' to '%s' in '%s'",
"no_exist" : "%s does not exist",
"no_line" : "file '%s' should have a line '%s' somewhere",
- "no_dir2" : "can't find %s for %s in cobber.conf",
+ "no_dir2" : "can't find %s for %s in cobber.conf",
"no_cfg" : "could not find cobber.conf, recreating",
"bad_param" : "at least one parameter is missing for this function",
"empty_list" : "(Empty)",
@@ -46,9 +46,9 @@ msg_table = {
"no_kernel" : "the kernel needs to be a directory containing a kernel, or a full path. Kernels must be named just 'vmlinuz' or in the form 'vmlinuz-AA.BB.CC-something'",
"no_initrd" : "the initrd needs to be a directory containing an initrd, or a full path. Initrds must be named just 'initrd.img' or in the form 'initrd-AA.BB.CC-something.img",
"check_ok" : """
-No setup problems found.
+No setup problems found.
-Manual editing of /etc/dhcpd.conf and /etc/cobber.conf is suggested to tailor them to your specific configuration. Your dhcpd.conf has some PXE related information in it, but it's imposible to tell automatically that it's totally correct in a general sense. We'll leave this up to you.
+Manual editing of /etc/dhcpd.conf and /etc/cobber.conf is suggested to tailor them to your specific configuration. Your dhcpd.conf has some PXE related information in it, but it's imposible to tell automatically that it's totally correct in a general sense. We'll leave this up to you.
Good luck.
""",
diff --git a/cobbler/sync.py b/cobbler/sync.py
index a31e262..ae20872 100644
--- a/cobbler/sync.py
+++ b/cobbler/sync.py
@@ -10,9 +10,9 @@ import sys
import traceback
import re
import shutil
+import yaml # from RHN's spec-tree (Howell/Evans)
import IPy
-import yaml
from msg import *
"""
@@ -28,7 +28,7 @@ class BootSync:
def sync(self,dry_run=False,verbose=True):
"""
- Syncs the current configuration file with the config tree.
+ Syncs the current configuration file with the config tree.
Using the Check().run_ functions previously is recommended
"""
self.dry_run = dry_run
@@ -53,7 +53,7 @@ class BootSync:
def configure_httpd(self):
"""
- Create a config file to Apache that will allow access to the
+ Create a config file to Apache that will allow access to the
cobbler infrastructure available over TFTP over HTTP also.
"""
if not os.path.exists("/etc/httpd/conf.d"):
@@ -82,7 +82,7 @@ class BootSync:
"""
for x in ["pxelinux.cfg","images","systems","distros","profiles","kickstarts"]:
path = os.path.join(self.api.config.tftpboot,x)
- self.rmtree(path, True)
+ self.rmtree(path, True)
self.mkdir(path)
def copy_distros(self):
@@ -140,7 +140,7 @@ class BootSync:
def build_trees(self):
"""
Now that kernels and initrds are copied and kickstarts are all valid,
- build the pxelinux.cfg tree, which contains a directory for each
+ build the pxelinux.cfg tree, which contains a directory for each
configured IP or MAC address. Also build a parallel 'xeninfo' tree
for xen-net-install info.
"""
@@ -182,7 +182,7 @@ class BootSync:
self.api.last_error = m("orphan_profile2")
raise "error"
distro = distros.find(profile.distro)
- if distro is None:
+ if distro is None:
self.api.last_error = m("orphan_system2")
raise "error"
f1 = self.get_pxelinux_filename(system.name)
@@ -208,7 +208,7 @@ class BootSync:
else:
self.api.last_error = m("err_resolv") % name
raise "error"
-
+
def write_pxelinux_file(self,filename,system,profile,distro):
"""
@@ -228,9 +228,9 @@ class BootSync:
self.tee(fd,"label linux\n")
self.tee(fd," kernel %s\n" % kernel_path)
kopts = self.blend_kernel_options((
- self.api.config.kernel_options,
- profile.kernel_options,
- distro.kernel_options,
+ self.api.config.kernel_options,
+ profile.kernel_options,
+ distro.kernel_options,
system.kernel_options
))
nextline = " append %s initrd=%s" % (kopts,initrd_path)
@@ -246,7 +246,7 @@ class BootSync:
def write_distro_file(self,filename,distro):
- """
+ """
Create distro information for xen-net-install
"""
fd = self.open_file(filename,"w+")
@@ -268,12 +268,12 @@ class BootSync:
profile.kickstart = "http://%s/cobbler/kickstarts/%s/ks.cfg" % (self.api.config.server, profile.name)
self.tee(fd,yaml.dump(profile.to_datastruct()))
self.close_file(fd)
-
+
def write_system_file(self,filename,system):
"""
Create system information for xen-net-install
- """
+ """
fd = self.open_file(filename,"w+")
self.tee(fd,yaml.dump(system.to_datastruct()))
self.close_file(fd)
@@ -285,7 +285,7 @@ class BootSync:
self.sync_log(text)
if not self.dry_run:
fd.write(text)
-
+
def open_file(self,filename,mode):
"""
For dry_run support: open a file if not in dry_run mode.
@@ -293,7 +293,7 @@ class BootSync:
if self.dry_run:
return None
return open(filename,mode)
-
+
def close_file(self,fd):
"""
For dry_run support: close a file if not in dry_run mode.
@@ -347,15 +347,15 @@ class BootSync:
print "dry_run | %s" % message
else:
print message
-
+
def blend_kernel_options(self, list_of_opts):
"""
Given a list of kernel options, take the values used by the
first argument in the list unless overridden by those in the
- second (or further on), according to --key=value formats.
+ second (or further on), according to --key=value formats.
- This is used such that we can have default kernel options
- in /etc and then distro, profile, and system options with various
+ This is used such that we can have default kernel options
+ in /etc and then distro, profile, and system options with various
levels of configurability.
"""
internal = {}
@@ -377,12 +377,12 @@ class BootSync:
data = internal[key]
if key == "ks" or key == "initrd" or key == "append":
# the user REALLY doesn't want to do this...
- continue
+ continue
if data == "":
results.append(key)
- else:
+ else:
results.append("%s=%s" % (key,internal[key]))
# end result is a new fragment of a kernel options string
return " ".join(results)
-
+
diff --git a/cobbler/util.py b/cobbler/util.py
index e8cdf3f..cdf7bbe 100644
--- a/cobbler/util.py
+++ b/cobbler/util.py
@@ -1,5 +1,5 @@
# Misc heavy lifting functions for cobbler
-#
+#
# Michael DeHaan <mdehaan@redhat.com>
import config
@@ -59,7 +59,7 @@ class BootUtil:
try:
return socket.gethostbyname(strdata)
except:
- return None
+ return None
def find_matching_files(self,directory,regex):
@@ -68,17 +68,17 @@ class BootUtil:
Can't use glob directly as glob doesn't take regexen.
"""
files = glob.glob(os.path.join(directory,"*"))
- results = []
+ results = []
for f in files:
if regex.match(os.path.basename(f)):
results.append(f)
return results
-
+
def find_highest_files(self,directory,unversioned,regex):
"""
Find the highest numbered file (kernel or initrd numbering scheme)
- in a given directory that matches a given pattern. Used for
+ in a given directory that matches a given pattern. Used for
auto-booting the latest kernel in a directory.
"""
files = self.find_matching_files(directory, regex)
@@ -87,12 +87,12 @@ class BootUtil:
av = get_numbers.search(os.path.basename(a)).groups()
bv = get_numbers.search(os.path.basename(b)).groups()
if av[0]<bv[0]: return -1
- elif av[0]>bv[0]: return 1
+ elif av[0]>bv[0]: return 1
elif av[1]<bv[1]: return -1
elif av[1]>bv[1]: return 1
elif av[2]<bv[2]: return -1
elif av[2]>bv[2]: return 1
- return 0
+ return 0
if len(files) > 0:
return sorted(files, sort)[-1]
else:
@@ -102,13 +102,13 @@ class BootUtil:
if os.path.exists(last_chance):
return last_chance
return None
-
+
def find_kernel(self,path):
"""
Given a directory or a filename, find if the path can be made
to resolve into a kernel, and return that full path if possible.
- """
+ """
if os.path.isfile(path):
filename = os.path.basename(path)
if self.re_kernel.match(filename):
@@ -122,7 +122,7 @@ class BootUtil:
def find_initrd(self,path):
"""
- Given a directory or a filename, see if the path can be made
+ Given a directory or a filename, see if the path can be made
to resolve into an intird, return that full path if possible.
"""
# FUTURE: add another function to see if kernel and initrd have matched numbers (and throw a warning?)
@@ -133,9 +133,9 @@ class BootUtil:
if filename == "initrd.img" or filename == "initrd":
return path
elif os.path.isdir(path):
- return self.find_highest_files(path,"initrd.img",self.re_initrd)
+ return self.find_highest_files(path,"initrd.img",self.re_initrd)
return None
-
+
def find_kickstart(self,url):
"""
diff --git a/examples/example_ks b/examples/example_ks
index 3da6099..438be2a 100644
--- a/examples/example_ks
+++ b/examples/example_ks
@@ -1,10 +1,10 @@
#platform=x86, AMD64, or Intel EM64T
# System authorization information
-auth --useshadow --enablemd5
+auth --useshadow --enablemd5
# System bootloader configuration
bootloader --location=mbr
# Partition clearing information
-clearpart --linux --initlabel
+clearpart --linux --initlabel
# Use graphical install
text
# Firewall configuration
diff --git a/tests/tests.py b/tests/tests.py
index 1a54149..440c588 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -14,11 +14,6 @@ sys.path.append('./cobbler')
import api
import config
-# rewrite default configuration locations so anyone
-# with a real config won't get hurt
-config.global_settings_file = "./tests/etc/cobbler.conf"
-config.global_state_file = "./tests/var/lib/cobbler/cobbler.conf"
-
FAKE_INITRD="/tmp/initrd-2.6.15-1.2054_FAKE.img"
FAKE_INITRD2="/tmp/initrd-2.5.16-2.2055_FAKE.img"
FAKE_INITRD3="/tmp/initrd-1.8.18-3.9999_FAKE.img"
@@ -80,10 +75,10 @@ class Utilities(BootTest):
self.assertFalse(self.api.utils.find_kernel("/etc/fstab"))
self.assertFalse(self.api.utils.find_initrd("filedoesnotexist"))
self.assertTrue(self.api.utils.find_initrd("/tmp") == FAKE_INITRD)
-
+
def test_kickstart_scan(self):
# we don't check to see if kickstart files look like anything
- # so this will pass
+ # so this will pass
self.assertTrue(self.api.utils.find_kickstart(FAKE_INITRD) is None)
self.assertTrue(self.api.utils.find_kickstart("filedoesnotexist") is None)
self.assertTrue(self.api.utils.find_kickstart("/tmp") == None)
@@ -100,7 +95,7 @@ class Utilities(BootTest):
self.assertTrue(self.api.utils.is_ip("127.0.0.1"))
self.assertTrue(self.api.utils.is_ip("192.168.1.1"))
self.assertFalse(self.api.utils.is_ip("00:C0:B7:7E:55:50"))
- self.assertFalse(self.api.utils.is_ip(self.hostname))
+ self.assertFalse(self.api.utils.is_ip(self.hostname))
class Additions(BootTest):
@@ -122,7 +117,7 @@ class Additions(BootTest):
self.assertFalse(distro.set_initrd("filedoesntexist"))
self.assertFalse(self.api.get_distros().add(distro))
self.assertFalse(self.api.get_distros().find("testdistro3"))
-
+
def test_invalid_profile_non_referenced_distro(self):
profile = self.api.new_profile()
self.assertTrue(profile.set_name("testprofile11"))
@@ -161,7 +156,7 @@ class Additions(BootTest):
self.assertFalse(profile.set_xen_file_size("54321.23"))
# macs must be properly formatted
self.assertTrue(profile.set_xen_mac("AA:BB:CC:DD:EE:FF"))
- self.assertFalse(profile.set_xen_mac("AA-BB-CC-DD-EE-FF"))
+ self.assertFalse(profile.set_xen_mac("AA-BB-CC-DD-EE-FF"))
# paravirt must be 'true' or 'false'
self.assertFalse(profile.set_xen_mac("cowbell"))
self.assertTrue(profile.set_xen_paravirt('true'))
@@ -203,7 +198,7 @@ class Additions(BootTest):
self.assertFalse(self.api.get_systems().add(system))
class Deletions(BootTest):
-
+
def test_invalid_delete_profile_doesnt_exist(self):
self.assertFalse(self.api.get_profiles().remove("doesnotexist"))
@@ -220,7 +215,7 @@ class Deletions(BootTest):
def test_invalid_delete_distro_would_orphan_profile(self):
self.make_basic_config()
self.assertFalse(self.api.get_distros().remove("testdistro0"))
-
+
def test_working_deletes(self):
self.api.clear()
self.make_basic_config()
@@ -232,23 +227,8 @@ class Deletions(BootTest):
self.assertFalse(self.api.get_profiles().find("testprofile0"))
self.assertFalse(self.api.get_distros().find("testdistro0"))
-class TestSerialization(BootTest):
-
- def test_serialization(self):
- self.make_basic_config()
- self.api.serialize()
- self.api.clear()
- 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.api.deserialize()
- self.assertTrue(self.api.get_systems().find(self.hostname))
- self.assertTrue(self.api.get_profiles().find("testprofile0"))
- self.assertTrue(self.api.get_distros().find("testdistro0"))
-
-
class TestCheck(BootTest):
-
+
def test_check(self):
# we can't know if it's supposed to fail in advance
# (ain't that the halting problem), but it shouldn't ever
@@ -256,7 +236,7 @@ class TestCheck(BootTest):
self.api.check()
class TestSync(BootTest):
-
+
def test_dry_run(self):
# dry_run just *shows* what is done, it doesn't apply the config
# the test here is mainly for coverage, we do not test
@@ -271,7 +251,7 @@ class TestSync(BootTest):
pass
class TestListings(BootTest):
-
+
def test_listings(self):
# check to see if the collection listings output something.
# this is a minimal check, mainly for coverage, not validity