util.py 4.51 KB
Newer Older
1
# -*- coding: utf-8 -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################
29

30 31
import errno
import os
32 33
import socket
import struct
34
import subprocess
35
import sqlite3
Bryton Lacquement's avatar
Bryton Lacquement committed
36
from xml_marshaller.xml_marshaller import dumps, loads
37

38

39
def mkdir_p(path, mode=0o700):
40 41 42 43 44 45 46 47 48 49 50 51 52 53
    """\
    Creates a directory and its parents, if needed.

    NB: If the directory already exists, it does not change its permission.
    """

    try:
        os.makedirs(path, mode)
    except OSError as exc:
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        else:
            raise

54

55
def chownDirectory(path, uid, gid):
56 57 58
  if os.getuid() != 0:
    # we are probably inside of a webrunner
    return
59
  # find /opt/slapgrid -not -user 1000 -exec chown slapsoft:slapsoft {} +
60 61
  subprocess.check_call([
      '/usr/bin/find', path, '-not', '-user', str(uid), '-exec',
62
      '/bin/chown', '%s:%s' % (uid, gid), '{}', '+'
63
  ])
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79


def parse_certificate_key_pair(html):
  """
  Extract (certificate, key) pair from an HTML page received by SlapOS Master.
  """

  c_start = html.find("Certificate:")
  c_end = html.find("</textarea>", c_start)
  certificate = html[c_start:c_end]

  k_start = html.find("-----BEGIN PRIVATE KEY-----")
  k_end = html.find("</textarea>", k_start)
  key = html[k_start:k_end]

  return certificate, key
80 81 82 83 84 85 86 87 88 89


def string_to_boolean(string):
  """
  Return True if the value of the "string" parameter can be parsed as True.
  Return False if the value of the "string" parameter can be parsed as False.
  Otherwise, Raise.

  The parser is completely arbitrary, see code for actual implementation.
  """
Bryton Lacquement's avatar
Bryton Lacquement committed
90 91 92
  try:
    return ('false', 'true').index(string.lower())
  except Exception:
93
    raise ValueError('%s is neither True nor False.' % string)
94 95


96 97 98 99 100
def sqlite_connect(dburi, timeout=None):
  connect_kw = {}
  if timeout is not None:
    connect_kw['timeout'] = timeout
  conn = sqlite3.connect(dburi, **connect_kw)
101
  conn.text_factory = str       # allow 8-bit strings
102
  return conn
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

# The 3 functions below were imported from re6st:
# https://lab.nexedi.com/nexedi/re6stnet/blob/master/re6st/utils.py
def binFromRawIpv6(ip):
  ip1, ip2 = struct.unpack('>QQ', ip)
  return bin(ip1)[2:].rjust(64, '0') + bin(ip2)[2:].rjust(64, '0')

def binFromIpv6(ip):
  """
  convert an IPv6  to a 128 characters string containing 0 and 1
  e.g.: '2001:db8::'-> '001000000000000100001101101110000000000...000'
  """
  return binFromRawIpv6(socket.inet_pton(socket.AF_INET6, ip))

def ipv6FromBin(ip, suffix=''):
  """
  convert a string containing 0 and 1 to an IPv6
  if the string is less than 128 characters:
   * consider the string is the first bits
   * optionnaly can replace the last bits of the IP with a suffix (in binary string format)
  """
  suffix_len = 128 - len(ip)
  if suffix_len > 0:
    ip += suffix.rjust(suffix_len, '0')
  elif suffix_len:
    sys.exit("Prefix exceeds 128 bits")
  return socket.inet_ntop(socket.AF_INET6,
    struct.pack('>QQ', int(ip[:64], 2), int(ip[64:], 2)))

132 133 134
def lenNetmaskIpv6(netmask):
  return len(binFromIpv6(netmask).rstrip('0'))

Bryton Lacquement's avatar
Bryton Lacquement committed
135 136 137 138 139 140 141 142 143 144 145 146
# Used for Python 2-3 compatibility
if str is bytes:
  bytes2str = str2bytes = lambda s: s
  def unicode2str(s):
    return s.encode('utf-8')
else:
  def bytes2str(s):
    return s.decode()
  def str2bytes(s):
    return s.encode()
  def unicode2str(s):
    return s