cc1  v2.1
CC1 source code docs
 All Classes Namespaces Files Functions Variables Pages
utils.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 # @COPYRIGHT_begin
3 #
4 # Copyright [2010-2014] Institute of Nuclear Physics PAN, Krakow, Poland
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 # @COPYRIGHT_end
19 import subprocess
20 import sys
21 import shlex
22 
23 ##
24 # @package src.common.utils
25 #
26 
27 import datetime
28 import json
29 import logging
30 import os
31 import random
32 import re
33 import requests
34 import string
35 import xml.sax.handler
36 
37 
38 ##
39 #
40 # A simple function to converts XML data into native Python dictionary.
41 #
42 # @parameter{src} xml file content or stream
43 #
44 def xml2dict(src):
45 
46  non_id_char = re.compile('[^_0-9a-zA-Z]')
47 
48  def _name_mangle(name):
49  return non_id_char.sub('_', name)
50 
51  ##
52  #
53  # Adds to @prm{data} dictionary key named @prm{name} with value
54  # @prm{value}. If @prm{name} key with single-value already exists
55  # in @prm{data} key, it's converted into list. If it's already
56  # converted into list, yet another value is added.
57  # @parameter{data,dict} to which dictionary key should be added
58  # @parameter{name,string} name of the key
59  # @parameter{value,string} value of the key
60  #
61  def add_attr(data, name, value):
62  if name in data.keys():
63  # multiple attribute of the same name are represented by a list
64  children = data[name]
65  if not isinstance(children, list):
66  children = [children]
67  data[name] = children
68  children.append(value)
69  else:
70  data[name] = value
71 
72  class TreeBuilder(xml.sax.handler.ContentHandler):
73  def __init__(self):
74  self.stack = []
75  self.root = {}
76  self.current = self.root
77  self.text_parts = []
78 
79  def startElement(self, name, attrs):
80  self.stack.append((self.current, self.text_parts))
81  self.current = {}
82  self.text_parts = []
83  # xml attributes --> python attributes
84  for k, v in attrs.items():
85  add_attr(self.current, _name_mangle(k), v)
86 
87  def endElement(self, name):
88  text = ''.join(self.text_parts).strip()
89  if text:
90  self.current['text'] = text
91  if self.current:
92  obj = self.current
93  else:
94  # a text only node is simply represented by the string
95  obj = text or ''
96  self.current, self.text_parts = self.stack.pop()
97  add_attr(self.current, _name_mangle(name), obj)
98 
99  def characters(self, content):
100  self.text_parts.append(content)
101 
102  builder = TreeBuilder()
103  if isinstance(src, basestring):
104  xml.sax.parseString(src, builder)
105  else:
106  xml.sax.parse(src, builder)
107  return builder.root
108 
109 
110 ##
111 #
112 # Convert ip from long-integer to standard, human-readable form.
113 #
114 def ip_itos(ip_int):
115  ip = []
116  ip.append(str((ip_int & 0xFF000000L) >> 24))
117  ip.append(str((ip_int & 0x00FF0000L) >> 16))
118  ip.append(str((ip_int & 0x0000FF00L) >> 8))
119  ip.append(str(ip_int & 0x000000FFL))
120  return '.'.join(ip)
121 
122 
123 ##
124 #
125 # Convert ip from human-readable form to long-integer.
126 # @parameter{ip,string} IP address with 4 dot-separated numbers (0-255).
127 # @returns{long int} number representation of the given @prm{ip}
128 #
129 def ip_stoi(ip):
130  bins = ip.split('.')
131  return long(bins[0]) << 24 | int(bins[1]) << 16 | int(bins[2]) << 8 | int(bins[3])
132 
133 
134 ##
135 #
136 # Convert ip from string to mac address.
137 # @parameter{ip,string} IP address with 4 dot-separated numbers (0-255).
138 # @returns{string} mac address
139 #
140 def ip_stomac(ip):
141  return '02:00:%02x:%02x:%02x:%02x' % tuple([int(x) for x in ip.split('.')])
142 
143 
144 ##
145 #
146 # Generates random password of given length. The password consists
147 # of symbols drawn from the set of specified character groups (@prm{chars})
148 # and (optionally) of extra characters specified in (@prm{extra_chars}).
149 # @parameter{length,int} length of the requested password
150 # @parameter{chars,list(string)} may contain @str{letters}, @str{digits}, or
151 # @str{special}.
152 # @parameter{extra_chars,list(char)} list of extra characters for password
153 # composition.
154 # @returns{string} password composed of @prm{length} symbols picked from
155 # specified character set
156 #
157 def password_gen(length, chars=['letters', 'digits', 'special'], extra_chars=None):
158  pool = ''
159  if 'letters' in chars:
160  pool += string.ascii_letters
161  if 'digits' in chars:
162  pool += string.digits
163  if 'special' in chars:
164  pool += string.punctuation
165  if extra_chars:
166  pool += extra_chars
167 
168  seen = set()
169  pool = [x for x in pool if x not in seen and not seen.add(x)]
170 
171  random.seed = (os.urandom(1024))
172  return ''.join(random.choice(pool) for i in range(length))
173 
174 
175 ##
176 #
177 # Convert object to json format
178 #
180 
181  DATE_FORMAT = "%d.%m.%Y"
182  TIME_FORMAT = "%H:%M:%S"
183 
184  if type(o) == datetime.date:
185  return o.strftime(DATE_FORMAT)
186  elif type(o) == datetime.time:
187  return o.strftime(TIME_FORMAT)
188  elif type(o) == datetime.datetime:
189  return o.strftime("%s, %s" % (DATE_FORMAT, TIME_FORMAT))
190  else:
191  return json.dumps(o)
192 
193 
194 class ServerProxy(object):
195  def __init__(self, server_address):
196  self.server_address = server_address
197 
198  def send_request(self, url, log=True, **data):
199  logger = logging.getLogger('request')
200  data = json.dumps(data, default=json_convert)
201  if log:
202  logger.info("called %s/%s body: %s" % (self.server_address, url, data))
203 
204  response = requests.post("%s/%s" % (self.server_address, url), data=data)
205 
206  if not response.ok:
207  logger.error("HTTP ERROR: code: %s data: %s" % (response.status_code, response.text[:100]))
208  raise Exception("Status %s failed to call function" % response.status_code)
209  response = json.loads(response.text)
210  if log:
211  logger.info("response from %s/%s is:\n%s" % (self.server_address, url, json.dumps(response, indent=4)))
212  if not isinstance(response, dict):
213  logger.error("Returned object is %s expected dict. Data: %s" % (type(response), response))
214  raise Exception("Returned object is %s expected dict" % type(response))
215  if 'status' not in response or 'data' not in response:
216  logger.error("Returned object is malformatted: %s" % response)
217  raise Exception("Returned object is malformatted: %s" % response)
218  return response
219 
220 
221 def subcall(command, log=None, err_log=None, std_log=None, err_msg=None, err_exit=True):
222  if not (std_log or err_log):
223  std_log = log
224  err_log = log
225  r = subprocess.call(command, shell=True, stdout=std_log, stderr=err_log)
226  if r > 0:
227  if err_msg:
228  START_MSG = '\033[91m' if err_exit else '\033[93m'
229  END_MSG = '\033[0m'
230  print "%s%s%s" % (START_MSG, err_msg, END_MSG)
231  if err_exit:
232  sys.exit(1)
233  return r
234