~lutostag/ubuntu/trusty/maas/1.5.4+keystone

« back to all changes in this revision

Viewing changes to etc/maas/commissioning-user-data

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2013-03-04 11:49:44 UTC
  • mto: This revision was merged to the branch mainline in revision 25.
  • Revision ID: package-import@ubuntu.com-20130304114944-azcvu9anlf8mizpa
Tags: upstream-1.3+bzr1452+dfsg
ImportĀ upstreamĀ versionĀ 1.3+bzr1452+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/bin/sh
2
 
#
3
 
# This script carries inside it multiple files.  When executed, it creates
4
 
# the files into a temporary directory, and then calls the 'main' function.
5
 
#
6
 
# main does a run-parts of all "scripts" and then calls home to maas with
7
 
# maas-signal, posting output of each of the files added with add_script().
8
 
#
9
 
####  IPMI setup  ######
10
 
# If IPMI network settings have been configured statically, you can
11
 
# make them DHCP. If 'true', the IPMI network source will be changed
12
 
# to DHCP.
13
 
IPMI_CHANGE_STATIC_TO_DHCP="false"
14
 
 
15
 
# In certain hardware, the parameters for the ipmi_si kernel module
16
 
# might need to be specified. If you wish to send parameters, uncomment
17
 
# the following line.
18
 
#IPMI_SI_PARAMS="type=kcs ports=0xca2"
19
 
 
20
 
#### script setup ######
21
 
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX")
22
 
SCRIPTS_D="${TEMP_D}/scripts"
23
 
IPMI_CONFIG_D="${TEMP_D}/ipmi.d"
24
 
BIN_D="${TEMP_D}/bin"
25
 
OUT_D="${TEMP_D}/out"
26
 
PATH="$BIN_D:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
27
 
trap cleanup EXIT
28
 
 
29
 
mkdir -p "$BIN_D" "$OUT_D" "$SCRIPTS_D" "$IPMI_CONFIG_D"
30
 
 
31
 
### some utility functions ####
32
 
aptget() {
33
 
   DEBIAN_FRONTEND=noninteractive apt-get --assume-yes -q "$@" </dev/null
34
 
}
35
 
 
36
 
writefile() {
37
 
   cat > "$1"
38
 
   chmod "$2" "$1"
39
 
}
40
 
add_bin() {
41
 
   cat > "${BIN_D}/$1"
42
 
   chmod "${2:-755}" "${BIN_D}/$1"
43
 
}
44
 
add_script() {
45
 
   cat > "${SCRIPTS_D}/$1"
46
 
   chmod "${2:-755}" "${SCRIPTS_D}/$1"
47
 
}
48
 
add_ipmi_config() {
49
 
   cat > "${IPMI_CONFIG_D}/$1"
50
 
   chmod "${2:-644}" "${IPMI_CONFIG_D}/$1"
51
 
}
52
 
cleanup() {
53
 
   [ -n "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
54
 
}
55
 
 
56
 
find_creds_cfg() {
57
 
   local config="" file="" found=""
58
 
 
59
 
   # If the config location is set in environment variable, trust it.
60
 
   [ -n "${COMMISSIONING_CREDENTIALS_URL}" ] &&
61
 
      _RET="${COMMISSIONING_CREDENTIALS_URL}" && return
62
 
 
63
 
   # Go looking for local files written by cloud-init.
64
 
   for file in /etc/cloud/cloud.cfg.d/*cmdline*.cfg; do
65
 
      [ -f "$file" ] && _RET="$file" && return
66
 
   done
67
 
 
68
 
   local opt="" cmdline=""
69
 
   if [ -f /proc/cmdline ] && read cmdline < /proc/cmdline; then
70
 
      # Search through /proc/cmdline arguments:
71
 
      # cloud-config-url trumps url=
72
 
      for opt in $cmdline; do
73
 
         case "$opt" in
74
 
            url=*)
75
 
               found=${opt#url=};;
76
 
            cloud-config-url=*)
77
 
               _RET="${opt#*=}"
78
 
               return 0;;
79
 
         esac
80
 
      done
81
 
      [ -n "$found" ] && _RET="$found" && return 0
82
 
   fi
83
 
   return 1
84
 
}
85
 
 
86
 
signal() {
87
 
   maas-signal "--config=${CRED_CFG}" "$@"
88
 
}
89
 
 
90
 
fail() {
91
 
   [ -z "$CRED_CFG" ] || signal FAILED "$1"
92
 
   echo "FAILED: $1" 1>&2;
93
 
   exit 1
94
 
}
95
 
 
96
 
write_poweroff_job() {
97
 
   cat >/etc/init/maas-poweroff.conf <<EOF
98
 
   description "poweroff when maas task is done"
99
 
   start on stopped cloud-final
100
 
   console output
101
 
   task
102
 
   script
103
 
     [ ! -e /tmp/block-poweroff ] || exit 0
104
 
     poweroff
105
 
   end script
106
 
EOF
107
 
   # reload required due to lack of inotify in overlayfs (LP: #882147)
108
 
   initctl reload-configuration
109
 
}
110
 
 
111
 
main() {
112
 
   write_poweroff_job
113
 
 
114
 
   # Install tools and load modules.
115
 
   aptget update
116
 
   aptget install freeipmi-tools
117
 
   load_modules
118
 
 
119
 
   # The main function, actually execute stuff that is written below.
120
 
   local script total=0 creds=""
121
 
 
122
 
   find_creds_cfg ||
123
 
      fail "failed to find credential config"
124
 
   creds="$_RET"
125
 
 
126
 
   # Get remote credentials into a local file.
127
 
   case "$creds" in
128
 
      http://*|https://*)
129
 
         wget "$creds" -O "${TEMP_D}/my.creds" ||
130
 
            fail "failed to get credentials from $cred_cfg"
131
 
         creds="${TEMP_D}/my.creds"
132
 
         ;;
133
 
   esac
134
 
 
135
 
   # Use global name read by signal() and fail.
136
 
   CRED_CFG="$creds"
137
 
 
138
 
   # Power settings.
139
 
   local pargs=""
140
 
   if $IPMI_CHANGE_STATIC_TO_DHCP; then
141
 
      pargs="--dhcp-if-static"
142
 
   fi
143
 
   power_settings=$(maas-ipmi-autodetect --configdir "$IPMI_CONFIG_D" ${pargs})
144
 
   if [ ! -z "$power_settings" ]; then
145
 
      signal "--power-type=ipmi" "--power-parameters=${power_settings}" WORKING "finished [maas-ipmi-autodetect]"
146
 
   fi
147
 
 
148
 
   # Just get a count of how many scripts there are for progress reporting.
149
 
   for script in "${SCRIPTS_D}/"*; do
150
 
      [ -x "$script" -a -f "$script" ] || continue
151
 
      total=$(($total+1))
152
 
   done
153
 
 
154
 
   local cur=1 numpass=0 name="" failed=""
155
 
   for script in "${SCRIPTS_D}/"*; do
156
 
      [ -f "$script" -a -f "$script" ] || continue
157
 
      name=${script##*/}
158
 
      signal WORKING "starting ${script##*/} [$cur/$total]"
159
 
      "$script" > "${OUT_D}/${name}.out" 2> "${OUT_D}/${name}.err"
160
 
      ret=$?
161
 
      signal WORKING "finished $name [$cur/$total]: $ret"
162
 
      if [ $ret -eq 0 ]; then
163
 
          numpass=$(($numpass+1))
164
 
          failed="${failed} ${name}"
165
 
      fi
166
 
      cur=$(($cur+1))
167
 
   done
168
 
 
169
 
   # Get a list of all files created, ignoring empty ones.
170
 
   local fargs=""
171
 
   for file in "${OUT_D}/"*; do
172
 
      [ -f "$file" -a -s "$file" ] || continue
173
 
      fargs="$fargs --file=${file##*/}"
174
 
   done
175
 
 
176
 
   if [ $numpass -eq $total ]; then
177
 
      ( cd "${OUT_D}" &&
178
 
         signal $fargs OK "finished [$numpass/$total]" )
179
 
      return 0
180
 
   else
181
 
      ( cd "${OUT_D}" &&
182
 
         signal $fargs OK "failed [$numpass/$total] ($failed)" )
183
 
      return $(($count-$numpass))
184
 
   fi
185
 
 
186
 
}
187
 
 
188
 
load_modules() {
189
 
   modprobe ipmi_msghandler
190
 
   modprobe ipmi_devintf
191
 
   modprobe ipmi_si ${IPMI_SI_PARAMS}
192
 
   udevadm settle
193
 
}
194
 
 
195
 
### begin writing files ###
196
 
add_script "01-lshw" <<"END_LSHW"
197
 
#!/bin/sh
198
 
lshw -xml
199
 
END_LSHW
200
 
 
201
 
add_ipmi_config "02-global-config.ipmi" <<"END_IPMI_CONFIG"
202
 
Section Lan_Channel
203
 
        Volatile_Access_Mode                    Always_Available
204
 
        Volatile_Enable_User_Level_Auth         Yes
205
 
        Volatile_Channel_Privilege_Limit        Administrator
206
 
        Non_Volatile_Access_Mode                Always_Available
207
 
        Non_Volatile_Enable_User_Level_Auth     Yes
208
 
        Non_Volatile_Channel_Privilege_Limit    Administrator
209
 
EndSection
210
 
END_IPMI_CONFIG
211
 
 
212
 
add_bin "maas-ipmi-autodetect" <<"END_MAAS_IPMI_AUTODETECT"
213
 
#!/usr/bin/python
214
 
import os
215
 
import commands
216
 
import glob
217
 
import re
218
 
import string
219
 
import random
220
 
import time
221
 
 
222
 
def detect_ipmi():
223
 
    # TODO: Detection could be improved.
224
 
    (status, output) = commands.getstatusoutput('ipmi-locate')
225
 
    show_re = re.compile('(IPMI\ Version:) (\d\.\d)')
226
 
    res = show_re.search(output)
227
 
    if res == None:
228
 
        found = glob.glob("/dev/ipmi[0-9]")
229
 
        if len(found):
230
 
            return (True, "UNKNOWN: %s" % " ".join(found))
231
 
        return (False, "")
232
 
    return (True, res.group(2))
233
 
 
234
 
def is_ipmi_dhcp():
235
 
    (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address_Source"')
236
 
    show_re = re.compile('IP_Address_Source\s+Use_DHCP')
237
 
    res = show_re.search(output)
238
 
    if res == None:
239
 
        return False
240
 
    return True
241
 
 
242
 
def set_ipmi_network_source(source):
243
 
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="Lan_Conf:IP_Address_Source=%s"' % source)
244
 
 
245
 
def get_ipmi_ip_address():
246
 
    (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address"')
247
 
    show_re = re.compile('([0-9]{1,3}[.]?){4}')
248
 
    res = show_re.search(output)
249
 
    return res.group()
250
 
 
251
 
def get_ipmi_user_number(user):
252
 
    for i in range(1, 17):
253
 
        ipmi_user_number = "User%s" % i
254
 
        (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="%s:Username"' % ipmi_user_number)
255
 
        if user in output:
256
 
            return ipmi_user_number
257
 
    return None
258
 
 
259
 
def commit_ipmi_user_settings(user, password):
260
 
    ipmi_user_number = get_ipmi_user_number(user)
261
 
    if ipmi_user_number is None:
262
 
        (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="User10:Username=%s"' % user)
263
 
        ipmi_user_number = get_ipmi_user_number(user)
264
 
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Password=%s"' % (ipmi_user_number, password))
265
 
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Enable_User=Yes"' % ipmi_user_number)
266
 
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Enable_IPMI_Msgs=Yes"' % ipmi_user_number)
267
 
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Privilege_Limit=Administrator"' % ipmi_user_number)
268
 
 
269
 
def commit_ipmi_settings(config):
270
 
    (status, output) = commands.getstatusoutput('bmc-config --commit --filename %s' % config)
271
 
 
272
 
def get_maas_power_settings(user, password, ipaddress):
273
 
    return "%s,%s,%s" % (user, password, ipaddress)
274
 
 
275
 
def generate_random_password(min=8,max=15):
276
 
    length=random.randint(min,max)
277
 
    letters=string.ascii_letters+string.digits
278
 
    return ''.join([random.choice(letters) for _ in range(length)])
279
 
 
280
 
def main():
281
 
 
282
 
    import argparse
283
 
 
284
 
    parser = argparse.ArgumentParser(
285
 
        description='send config file to modify IPMI settings with')
286
 
    parser.add_argument("--configdir", metavar="folder",
287
 
        help="specify config file", default=None)
288
 
    parser.add_argument("--dhcp-if-static", action="store_true",
289
 
        dest="dhcp", help="specify config file", default=False)
290
 
 
291
 
    args = parser.parse_args()
292
 
 
293
 
    # Check whether IPMI exists or not.
294
 
    (status, ipmi_version) = detect_ipmi()
295
 
    if status != True:
296
 
        # if False, then failed to detect ipmi
297
 
        exit(1)
298
 
 
299
 
    # Check whether IPMI is being set to DHCP. If it is not, and
300
 
    # '--dhcp-if-static' has been passed,  Set it to IPMI to DHCP.
301
 
    if not is_ipmi_dhcp() and args.dhcp:
302
 
        set_ipmi_network_source("Use_DHCP")
303
 
        # allow IPMI 120 seconds to obtain an IP address
304
 
        time.sleep(120)
305
 
 
306
 
    # create user/pass
307
 
    IPMI_MAAS_USER="maas"
308
 
    IPMI_MAAS_PASSWORD=generate_random_password()
309
 
 
310
 
    # Configure IPMI user/password
311
 
    commit_ipmi_user_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD)
312
 
 
313
 
    # Commit other IPMI settings
314
 
    if args.configdir:
315
 
        for file in os.listdir(args.configdir):
316
 
            commit_ipmi_settings(os.path.join(args.configdir, file))
317
 
 
318
 
    # get the IP address
319
 
    IPMI_IP_ADDRESS = get_ipmi_ip_address()
320
 
    if IPMI_IP_ADDRESS == "0.0.0.0":
321
 
        # if IPMI_IP_ADDRESS is 0.0.0.0, wait 60 seconds and retry.
322
 
        set_ipmi_network_source("Static")
323
 
        time.sleep(2)
324
 
        set_ipmi_network_source("Use_DHCP")
325
 
        time.sleep(60)
326
 
        IPMI_IP_ADDRESS = get_ipmi_ip_address()
327
 
 
328
 
    if IPMI_IP_ADDRESS is None or IPMI_IP_ADDRESS == "0.0.0.0":
329
 
        # Exit (to not set power params in MAAS) if no IPMI_IP_ADDRESS
330
 
        # has been detected
331
 
        exit(1)
332
 
 
333
 
    print get_maas_power_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS)
334
 
 
335
 
if __name__ == '__main__':
336
 
    main()
337
 
END_MAAS_IPMI_AUTODETECT
338
 
 
339
 
add_bin "maas-signal" <<"END_MAAS_SIGNAL"
340
 
#!/usr/bin/python
341
 
 
342
 
from email.utils import parsedate
343
 
import mimetypes
344
 
import oauth.oauth as oauth
345
 
import os.path
346
 
import random
347
 
import string
348
 
import sys
349
 
import time
350
 
import urllib2
351
 
import yaml
352
 
import json
353
 
 
354
 
MD_VERSION = "2012-03-01"
355
 
VALID_STATUS = ("OK", "FAILED", "WORKING")
356
 
POWER_TYPES = ("ipmi", "virsh", "ether_wake")
357
 
 
358
 
 
359
 
def _encode_field(field_name, data, boundary):
360
 
    return ('--' + boundary,
361
 
            'Content-Disposition: form-data; name="%s"' % field_name,
362
 
            '', str(data))
363
 
 
364
 
 
365
 
def _encode_file(name, fileObj, boundary):
366
 
    return ('--' + boundary,
367
 
            'Content-Disposition: form-data; name="%s"; filename="%s"' %
368
 
                (name, name),
369
 
            'Content-Type: %s' % _get_content_type(name),
370
 
            '', fileObj.read())
371
 
 
372
 
 
373
 
def _random_string(length):
374
 
    return ''.join(random.choice(string.letters) for ii in range(length + 1))
375
 
 
376
 
 
377
 
def _get_content_type(filename):
378
 
    return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
379
 
 
380
 
 
381
 
def encode_multipart_data(data, files):
382
 
    """Create a MIME multipart payload from L{data} and L{files}.
383
 
 
384
 
    @param data: A mapping of names (ASCII strings) to data (byte string).
385
 
    @param files: A mapping of names (ASCII strings) to file objects ready to
386
 
        be read.
387
 
    @return: A 2-tuple of C{(body, headers)}, where C{body} is a a byte string
388
 
        and C{headers} is a dict of headers to add to the enclosing request in
389
 
        which this payload will travel.
390
 
    """
391
 
    boundary = _random_string(30)
392
 
 
393
 
    lines = []
394
 
    for name in data:
395
 
        lines.extend(_encode_field(name, data[name], boundary))
396
 
    for name in files:
397
 
        lines.extend(_encode_file(name, files[name], boundary))
398
 
    lines.extend(('--%s--' % boundary, ''))
399
 
    body = '\r\n'.join(lines)
400
 
 
401
 
    headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
402
 
               'content-length': str(len(body))}
403
 
 
404
 
    return body, headers
405
 
 
406
 
 
407
 
def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
408
 
                  clockskew=0):
409
 
    consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
410
 
    token = oauth.OAuthToken(token_key, token_secret)
411
 
 
412
 
    timestamp = int(time.time()) + clockskew
413
 
 
414
 
    params = {
415
 
        'oauth_version': "1.0",
416
 
        'oauth_nonce': oauth.generate_nonce(),
417
 
        'oauth_timestamp': timestamp,
418
 
        'oauth_token': token.key,
419
 
        'oauth_consumer_key': consumer.key,
420
 
    }
421
 
    req = oauth.OAuthRequest(http_url=url, parameters=params)
422
 
    req.sign_request(oauth.OAuthSignatureMethod_PLAINTEXT(),
423
 
        consumer, token)
424
 
    return(req.to_header())
425
 
 
426
 
 
427
 
def geturl(url, creds, headers=None, data=None):
428
 
    # Takes a dict of creds to be passed through to oauth_headers,
429
 
    #   so it should have consumer_key, token_key, ...
430
 
    if headers is None:
431
 
        headers = {}
432
 
    else:
433
 
        headers = dict(headers)
434
 
 
435
 
    clockskew = 0
436
 
 
437
 
    def warn(msg):
438
 
        sys.stderr.write(msg + "\n")
439
 
 
440
 
    exc = Exception("Unexpected Error")
441
 
    for naptime in (1, 1, 2, 4, 8, 16, 32):
442
 
        if creds.get('consumer_key', None) != None:
443
 
            headers.update(oauth_headers(url,
444
 
                consumer_key=creds['consumer_key'],
445
 
                token_key=creds['token_key'],
446
 
                token_secret=creds['token_secret'],
447
 
                consumer_secret=creds['consumer_secret'],
448
 
                clockskew=clockskew))
449
 
        try:
450
 
            req = urllib2.Request(url=url, data=data, headers=headers)
451
 
            return(urllib2.urlopen(req).read())
452
 
        except urllib2.HTTPError as exc:
453
 
            if 'date' not in exc.headers:
454
 
                warn("date field not in %d headers" % exc.code)
455
 
                pass
456
 
            elif (exc.code == 401 or exc.code == 403):
457
 
                date = exc.headers['date']
458
 
                try:
459
 
                    ret_time = time.mktime(parsedate(date))
460
 
                    clockskew = int(ret_time - time.time())
461
 
                    warn("updated clock skew to %d" % clockskew)
462
 
                except:
463
 
                    warn("failed to convert date '%s'" % date)
464
 
        except Exception as exc:
465
 
            pass
466
 
 
467
 
        warn("request to %s failed. sleeping %d.: %s" % (url, naptime, exc))
468
 
        time.sleep(naptime)
469
 
 
470
 
    raise exc
471
 
 
472
 
 
473
 
def read_config(url, creds):
474
 
    if url.startswith("http://") or url.startswith("https://"):
475
 
        cfg_str = urllib2.urlopen(urllib2.Request(url=url))
476
 
    else:
477
 
        if url.startswith("file://"):
478
 
            url = url[7:]
479
 
        cfg_str = open(url,"r").read()
480
 
 
481
 
    cfg = yaml.safe_load(cfg_str)
482
 
 
483
 
    # Support reading cloud-init config for MAAS datasource.
484
 
    if 'datasource' in cfg:
485
 
        cfg = cfg['datasource']['MAAS']
486
 
 
487
 
    for key in creds.keys():
488
 
        if key in cfg and creds[key] == None:
489
 
            creds[key] = cfg[key]
490
 
 
491
 
def fail(msg):
492
 
    sys.stderr.write("FAIL: %s" % msg)
493
 
    sys.exit(1)
494
 
 
495
 
 
496
 
def main():
497
 
    """
498
 
    Call with single argument of directory or http or https url.
499
 
    If url is given additional arguments are allowed, which will be
500
 
    interpreted as consumer_key, token_key, token_secret, consumer_secret.
501
 
    """
502
 
    import argparse
503
 
    import pprint
504
 
 
505
 
    parser = argparse.ArgumentParser(
506
 
        description='Send signal operation and optionally post files to MAAS')
507
 
    parser.add_argument("--config", metavar="file",
508
 
        help="Specify config file", default=None)
509
 
    parser.add_argument("--ckey", metavar="key",
510
 
        help="The consumer key to auth with", default=None)
511
 
    parser.add_argument("--tkey", metavar="key",
512
 
        help="The token key to auth with", default=None)
513
 
    parser.add_argument("--csec", metavar="secret",
514
 
        help="The consumer secret (likely '')", default="")
515
 
    parser.add_argument("--tsec", metavar="secret",
516
 
        help="The token secret to auth with", default=None)
517
 
    parser.add_argument("--apiver", metavar="version",
518
 
        help="The apiver to use ("" can be used)", default=MD_VERSION)
519
 
    parser.add_argument("--url", metavar="url",
520
 
        help="The data source to query", default=None)
521
 
    parser.add_argument("--file", dest='files',
522
 
        help="File to post", action='append', default=[])
523
 
    parser.add_argument("--post", dest='posts',
524
 
        help="name=value pairs to post", action='append', default=[])
525
 
    parser.add_argument("--power-type", dest='power_type',
526
 
        help="Power type.", choices=POWER_TYPES, default=None)
527
 
    parser.add_argument("--power-parameters", dest='power_parms',
528
 
        help="Power parameters.", default=None)
529
 
 
530
 
    parser.add_argument("status",
531
 
        help="Status", choices=VALID_STATUS, action='store')
532
 
    parser.add_argument("message", help="Optional message",
533
 
        default="", nargs='?')
534
 
 
535
 
    args = parser.parse_args()
536
 
 
537
 
    creds = {'consumer_key': args.ckey, 'token_key': args.tkey,
538
 
        'token_secret': args.tsec, 'consumer_secret': args.csec,
539
 
        'metadata_url': args.url}
540
 
 
541
 
    if args.config:
542
 
        read_config(args.config, creds)
543
 
 
544
 
    url = creds.get('metadata_url', None)
545
 
    if not url:
546
 
        fail("URL must be provided either in --url or in config\n")
547
 
    url = "%s/%s/" % (url, args.apiver)
548
 
 
549
 
    params = {
550
 
        "op": "signal",
551
 
        "status": args.status,
552
 
        "error": args.message}
553
 
 
554
 
    for ent in args.posts:
555
 
        try:
556
 
           (key, val) = ent.split("=", 2)
557
 
        except ValueError:
558
 
           sys.stderr.write("'%s' had no '='" % ent)
559
 
           sys.exit(1)
560
 
        params[key] = val
561
 
 
562
 
    if args.power_parms is not None:
563
 
        params["power_type"] = args.power_type
564
 
        power_parms = dict(
565
 
            power_user=args.power_parms.split(",")[0],
566
 
            power_pass=args.power_parms.split(",")[1],
567
 
            power_address=args.power_parms.split(",")[2]
568
 
            )
569
 
        params["power_parameters"] = json.dumps(power_parms)
570
 
 
571
 
    files = {}
572
 
    for fpath in args.files:
573
 
        files[os.path.basename(fpath)] = open(fpath, "r")
574
 
 
575
 
    data, headers = encode_multipart_data(params, files)
576
 
 
577
 
    exc = None
578
 
    msg = ""
579
 
 
580
 
    try:
581
 
        payload = geturl(url, creds=creds, headers=headers, data=data)
582
 
        if payload != "OK":
583
 
            raise TypeError("Unexpected result from call: %s" % payload)
584
 
        else:
585
 
            msg = "Success"
586
 
    except urllib2.HTTPError as exc:
587
 
        msg = "http error [%s]" % exc.code
588
 
    except urllib2.URLError as exc:
589
 
        msg = "url error [%s]" % exc.reason
590
 
    except socket.timeout as exc:
591
 
        msg = "socket timeout [%s]" % exc
592
 
    except TypeError as exc:
593
 
        msg = exc.message
594
 
    except Exception as exc:
595
 
        msg = "unexpected error [%s]" % exc
596
 
 
597
 
    sys.stderr.write("%s\n" % msg)
598
 
    sys.exit((exc is None))
599
 
 
600
 
if __name__ == '__main__':
601
 
    main()
602
 
END_MAAS_SIGNAL
603
 
 
604
 
main
605
 
exit