diff --git a/icinga2.spec b/icinga2.spec index cb6130cd3cb..4d9b305781f 100644 --- a/icinga2.spec +++ b/icinga2.spec @@ -26,6 +26,9 @@ %define apachegroup apache %if 0%{?el5}%{?el6} %define use_systemd 0 +%{!?__python2: %global __python2 /usr/bin/python2} +%{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} +%{!?python2_sitearch: %global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} %else # fedora and el>=7 %define use_systemd 1 @@ -168,6 +171,16 @@ Icinga 1.x Classic UI Standalone configuration with locations for Icinga 2. +%package -n python-icinga2 +Summary: Python modules for Icinga 2 +Group: Application/System +BuildRequires: python-devel +BuildRequires: python-setuptools + +%description -n python-icinga2 +Python modules and utility scripts for Icinga 2. + + %prep %setup -q -n %{name}-%{version} @@ -207,7 +220,10 @@ cmake $CMAKE_OPTS . make %{?_smp_mflags} -rm -f components/db_ido_*sql/schema/upgrade/.gitignore +pushd python +%{__python2} setup.py build +popd + %install [ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot} @@ -233,6 +249,10 @@ mkdir -p "%{buildroot}%{_localstatedir}/adm/fillup-templates/" mv "%{buildroot}%{_sysconfdir}/sysconfig/%{name}" "%{buildroot}%{_localstatedir}/adm/fillup-templates/sysconfig.%{name}" %endif +pushd python +%{__python2} setup.py install --skip-build --root $RPM_BUILD_ROOT +popd + %clean [ "%{buildroot}" != "/" ] && [ -d "%{buildroot}" ] && rm -rf %{buildroot} diff --git a/python/icinga2/__init__.py b/python/icinga2/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/icinga2/commands/__init__.py b/python/icinga2/commands/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/icinga2/commands/list_objects.py b/python/icinga2/commands/list_objects.py new file mode 100644 index 00000000000..9007e15d053 --- /dev/null +++ b/python/icinga2/commands/list_objects.py @@ -0,0 +1,2 @@ +def main(): + print "Hello World!" diff --git a/python/icinga2/util/__init__.py b/python/icinga2/util/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/icinga2/util/netstring.py b/python/icinga2/util/netstring.py new file mode 100644 index 00000000000..cdce9a827df --- /dev/null +++ b/python/icinga2/util/netstring.py @@ -0,0 +1,305 @@ +#!/usr/bin/python +# netstring.py - Netstring encoding/decoding routines. +# Version 1.1 - July 2003 +# http://www.dlitz.net/software/python-netstring/ +# +# Copyright (c) 2003 Dwayne C. Litzenberger +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# HISTORY: +# +# Changes between 1.0 and 1.1: +# - Renamed Reader to BaseReader. Use FileReader and StringReader instead. +# - Added BaseReader.readskip() +# - Switched to saner stream reading semantics. Now the stream is not read +# until information is requested which requires it to be read. +# - Added split() +# + +"""Conversions to/from netstring format. + +The netstring format is defined in http://cr.yp.to/proto/netstrings.txt +(or http://www.dlitz.net/proto/netstrings-abnf.txt if you prefer ABNF) + +Classes: + + BaseReader (not to be used directly) + FileReader + StringReader + +Functions: + + dump + dumps + load + loads + split + +Misc variables: + + maxintlen - Maximum number of digits when reading integers + +""" + +__all__ = ['BaseReader', 'FileReader', 'StringReader', + 'dump', 'dumps', 'load', 'loads', 'split'] + +from __future__ import generators +import StringIO + +maxintlen = 999 # Maximum number of digits when reading integers + # This allows numbers up to 10**1000 - 1, which should + # be large enough for most applications. :-) + + +def dump(s, file): + """dump(s, file) -> None + +Writes the string s as a netstring to file. +""" + file.write(dumps(s)) + + +def dumps(s): + """dumps(s) -> string + +Encodes the string s as a netstring, and returns the result. +""" + return str(len(s)) + ":" + s + "," + + +def load(file, maxlen=None): + """load(file, maxlen=None) -> string + +Read a netstring from a file, and return the extracted netstring. + +If the parsed string would be longer than maxlen, OverflowError is raised. +""" + n = _readlen(file) + if maxlen is not None and n > maxlen: + raise OverflowError + retval = file.read(n) + #assert(len(retval) == n) + ch = file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + return retval + + +def loads(s, maxlen=None, returnUnparsed=False): + """loads(s, maxlen=None, returnUnparsed=False) -> string or (string, + string) + +Extract a netstring from a string. If returnUnparsed is false, return the +decoded netstring, otherwise return a tuple (parsed, unparsed) containing both +the parsed string and the remaining unparsed part of s. + +If the parsed string would be longer than maxlen, OverflowError is raised. +""" + f = StringIO.StringIO(s) + parsed = load(f, maxlen=maxlen) + if not returnUnparsed: + return parsed + unparsed = f.read() + return parsed, unparsed + + +def _readlen(file): + """_readlen(file) -> integer + +Read the initial "[length]:" of a netstring from file, and return the length. +""" + i = 0 + n = "" + ch = file.read(1) + while ch != ":": + if ch == "": + raise EOFError + elif not ch in "0123456789": + raise ValueError + n += ch + i += 1 + if i > maxintlen: + raise OverflowError + ch = file.read(1) + #assert(ch == ":") + return long(n) + + +def split(s): + """split(s) -> list of strings + +Return a list of the decoded netstrings in s. +""" + if s == "": + raise EOFError + retval = [] + unparsed = s + while unparsed != "": + parsed, unparsed = loads(unparsed, returnUnparsed=True) + retval.append(parsed) + return retval + + +class BaseReader: + """BaseReader(file, maxlen=None, blocksize=1024) -> BaseReader object + +Return a new BaseReader object. BaseReader allows reading a +netstring in blocks, instead of reading an netstring into memory at once. + +If BaseReader encounters a netstring which is larger than maxlen, it will return +OverflowError. BaseReader will also return ValueError if it encounters bad +formatting, or EOFError if an unexpected attempt is made to read beyond the +end of file. + +The state of BaseReader is undefined once any exception other than StopIteration +is raised. + +blocksize is the size of blocks to use when iterating over the BaseReader class. +You should not use BaseReader except when subclassing. Use FileReader or one +of the other *Reader classes instead. +""" + + def __init__(self, file, maxlen=None, blocksize=1024): + self._file = file + self._length = None + self._bytesleft = 0L + self._maxlen = maxlen + self._blocksize = blocksize + + def _readlen(self): + if self._length is None: + self._length = _readlen(self._file) + self._bytesleft = self._length + if self._maxlen is not None and self._length > self._maxlen: + raise OverflowError + # Handle the 0-byte case + if self._length == 0: + ch = self._file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + + def read(self, size=None): + """x.read([size]) -> string + +Works like .read. +""" + self._readlen() + if size is None or size > self._bytesleft: + size = self._bytesleft + if size == 0: + return "" + retval = self._file.read(size) + self._bytesleft -= len(retval) + if self._bytesleft == 0: + ch = self._file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + return retval + + def length(self): + """x.length() -> long + +Return the total length of the decoded string. +""" + self._readlen() + return self._length + + def bytesremaining(self): + """x.bytesremaining() -> long + +Return the number of decoded string bytes remaining to be read from the file. +""" + self._readlen() + return self._bytesleft + + def skip(self): + """x.skip() -> None + +Skip to the next netstring. +""" + self._readlen() + if self._bytesleft: + self._file.seek(self._bytesleft, 1) + ch = self._file.read(1) + if ch == "": + raise EOFError + elif ch != ",": + raise ValueError + self._bytesleft = 0L + self._length = None + + def readskip(self, size=None): + """x.readskip([size]) -> string + +Equivalent to x.read([size]); x.skip(). Returns whatever is returned by +x.read(). +""" + retval = self.read(size) + self.skip() + return retval + + def __iter__(self): + """x.__iter__() -> iterator + +Return a block of the decoded netstring. +""" + block = self.read(self._blocksize) + while block != "": + yield block + block = self.read(self._blocksize) + + def __len__(self): + """x.__len__() -> integer + +Return the total length of the decoded string. + +Note that this is limited to the maximum integer value. Use x.length() +wherever possible. +""" + return int(self.length()) + + +class FileReader(BaseReader): + """FileReader(file, ...) -> FileReader object + +Takes a file as input. See BaseReader.__doc__ for more information. +""" + pass + + +class StringReader(BaseReader): + """StringReader(s, ...) -> StringReader object + +Takes a string as input. See BaseReader.__doc__ for more information. +""" + def __init__(self, s, *args, **kwargs): + file = StringIO.StringIO(s) + return BaseReader.__init__(self, file, *args, **kwargs) + + +# vim:set tw=78 sw=4 ts=4 expandtab: diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 00000000000..97c20708711 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +import os, re +from setuptools import setup, find_packages + +def get_icinga2_version(): + spec = open(os.path.join(os.path.dirname(__file__), '..', 'icinga2.spec')).read() + m = re.search('^Version: (.*)$', spec, re.MULTILINE) + if not m: + return None + return m.group(1) + +setup( + name = 'icinga2', + version = get_icinga2_version(), + packages = find_packages(), + entry_points = { + 'console_scripts': [ 'icinga2-list-objects=icinga2.commands.list_objects:main' ] + } +) +