This file is indexed.

/etc/maas/commissioning-user-data is in maas-region-controller 1.2+bzr1373+dfsg-0ubuntu1~12.04.6.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  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
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
#!/bin/sh
#
# This script carries inside it multiple files.  When executed, it creates
# the files into a temporary directory, and then calls the 'main' function.
#
# main does a run-parts of all "scripts" and then calls home to maas with
# maas-signal, posting output of each of the files added with add_script().
#
####  IPMI setup  ######
# If IPMI network settings have been configured statically, you can
# make them DHCP. If 'true', the IPMI network source will be changed
# to DHCP.
IPMI_CHANGE_STATIC_TO_DHCP="false"

# In certain hardware, the parameters for the ipmi_si kernel module
# might need to be specified. If you wish to send parameters, uncomment
# the following line.
#IPMI_SI_PARAMS="type=kcs ports=0xca2"

#### script setup ######
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX")
SCRIPTS_D="${TEMP_D}/scripts"
IPMI_CONFIG_D="${TEMP_D}/ipmi.d"
BIN_D="${TEMP_D}/bin"
OUT_D="${TEMP_D}/out"
PATH="$BIN_D:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
trap cleanup EXIT

mkdir -p "$BIN_D" "$OUT_D" "$SCRIPTS_D" "$IPMI_CONFIG_D"

### some utility functions ####
aptget() {
   DEBIAN_FRONTEND=noninteractive apt-get --assume-yes -q "$@" </dev/null
}

writefile() {
   cat > "$1"
   chmod "$2" "$1"
}
add_bin() {
   cat > "${BIN_D}/$1"
   chmod "${2:-755}" "${BIN_D}/$1"
}
add_script() {
   cat > "${SCRIPTS_D}/$1"
   chmod "${2:-755}" "${SCRIPTS_D}/$1"
}
add_ipmi_config() {
   cat > "${IPMI_CONFIG_D}/$1"
   chmod "${2:-644}" "${IPMI_CONFIG_D}/$1"
}
cleanup() {
   [ -n "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}

find_creds_cfg() {
   local config="" file="" found=""

   # If the config location is set in environment variable, trust it.
   [ -n "${COMMISSIONING_CREDENTIALS_URL}" ] &&
      _RET="${COMMISSIONING_CREDENTIALS_URL}" && return

   # Go looking for local files written by cloud-init.
   for file in /etc/cloud/cloud.cfg.d/*cmdline*.cfg; do
      [ -f "$file" ] && _RET="$file" && return
   done

   local opt="" cmdline=""
   if [ -f /proc/cmdline ] && read cmdline < /proc/cmdline; then
      # Search through /proc/cmdline arguments:
      # cloud-config-url trumps url=
      for opt in $cmdline; do
         case "$opt" in
            url=*)
               found=${opt#url=};;
            cloud-config-url=*)
               _RET="${opt#*=}"
               return 0;;
         esac
      done
      [ -n "$found" ] && _RET="$found" && return 0
   fi
   return 1
}

signal() {
   maas-signal "--config=${CRED_CFG}" "$@"
}

fail() {
   [ -z "$CRED_CFG" ] || signal FAILED "$1"
   echo "FAILED: $1" 1>&2;
   exit 1
}

write_poweroff_job() {
   cat >/etc/init/maas-poweroff.conf <<EOF
   description "poweroff when maas task is done"
   start on stopped cloud-final
   console output
   task
   script
     [ ! -e /tmp/block-poweroff ] || exit 0
     poweroff
   end script
EOF
   # reload required due to lack of inotify in overlayfs (LP: #882147)
   initctl reload-configuration
}

main() {
   write_poweroff_job

   # Install tools and load modules.
   aptget update
   aptget install freeipmi-tools
   load_modules

   # The main function, actually execute stuff that is written below.
   local script total=0 creds=""

   find_creds_cfg ||
      fail "failed to find credential config"
   creds="$_RET"

   # Get remote credentials into a local file.
   case "$creds" in
      http://*|https://*)
         wget "$creds" -O "${TEMP_D}/my.creds" ||
            fail "failed to get credentials from $cred_cfg"
         creds="${TEMP_D}/my.creds"
         ;;
   esac

   # Use global name read by signal() and fail.
   CRED_CFG="$creds"

   # Power settings.
   local pargs=""
   if $IPMI_CHANGE_STATIC_TO_DHCP; then
      pargs="--dhcp-if-static"
   fi
   power_settings=$(maas-ipmi-autodetect --configdir "$IPMI_CONFIG_D" ${pargs})
   if [ ! -z "$power_settings" ]; then
      signal "--power-type=ipmi" "--power-parameters=${power_settings}" WORKING "finished [maas-ipmi-autodetect]"
   fi

   # Just get a count of how many scripts there are for progress reporting.
   for script in "${SCRIPTS_D}/"*; do
      [ -x "$script" -a -f "$script" ] || continue
      total=$(($total+1))
   done

   local cur=1 numpass=0 name="" failed=""
   for script in "${SCRIPTS_D}/"*; do
      [ -f "$script" -a -f "$script" ] || continue
      name=${script##*/}
      signal WORKING "starting ${script##*/} [$cur/$total]"
      "$script" > "${OUT_D}/${name}.out" 2> "${OUT_D}/${name}.err"
      ret=$?
      signal WORKING "finished $name [$cur/$total]: $ret"
      if [ $ret -eq 0 ]; then
          numpass=$(($numpass+1))
          failed="${failed} ${name}"
      fi
      cur=$(($cur+1))
   done

   # Get a list of all files created, ignoring empty ones.
   local fargs=""
   for file in "${OUT_D}/"*; do
      [ -f "$file" -a -s "$file" ] || continue
      fargs="$fargs --file=${file##*/}"
   done

   if [ $numpass -eq $total ]; then
      ( cd "${OUT_D}" &&
         signal $fargs OK "finished [$numpass/$total]" )
      return 0
   else
      ( cd "${OUT_D}" &&
         signal $fargs OK "failed [$numpass/$total] ($failed)" )
      return $(($count-$numpass))
   fi

}

load_modules() {
   modprobe ipmi_msghandler
   modprobe ipmi_devintf
   modprobe ipmi_si ${IPMI_SI_PARAMS}
   udevadm settle
}

### begin writing files ###
add_script "01-lshw" <<"END_LSHW"
#!/bin/sh
lshw -xml
END_LSHW

add_ipmi_config "02-global-config.ipmi" <<"END_IPMI_CONFIG"
Section Lan_Channel
	Volatile_Access_Mode			Always_Available
	Volatile_Enable_User_Level_Auth		Yes
	Volatile_Channel_Privilege_Limit	Administrator
	Non_Volatile_Access_Mode		Always_Available
	Non_Volatile_Enable_User_Level_Auth	Yes
	Non_Volatile_Channel_Privilege_Limit	Administrator
EndSection
END_IPMI_CONFIG

add_bin "maas-ipmi-autodetect" <<"END_MAAS_IPMI_AUTODETECT"
#!/usr/bin/python
import os
import commands
import glob
import re
import string
import random
import time

def detect_ipmi():
    # XXX: andreserl 2013-04-09 bug=1064527: Try to detect if node
    # is a Virtual Machine. If it is, do not try to detect IPMI.
    with open('/proc/cpuinfo', 'r') as cpuinfo:
        for line in cpuinfo:
            if line.startswith('model name') and 'QEMU' in line:
                return (False, None)

    (status, output) = commands.getstatusoutput('ipmi-locate')
    show_re = re.compile('(IPMI\ Version:) (\d\.\d)')
    res = show_re.search(output)
    if res == None:
        found = glob.glob("/dev/ipmi[0-9]")
        if len(found):
            return (True, "UNKNOWN: %s" % " ".join(found))
        return (False, "")
    return (True, res.group(2))

def is_ipmi_dhcp():
    (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address_Source"')
    show_re = re.compile('IP_Address_Source\s+Use_DHCP')
    res = show_re.search(output)
    if res == None:
        return False
    return True

def set_ipmi_network_source(source):
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="Lan_Conf:IP_Address_Source=%s"' % source)

def get_ipmi_ip_address():
    (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="Lan_Conf:IP_Address"')
    show_re = re.compile('([0-9]{1,3}[.]?){4}')
    res = show_re.search(output)
    return res.group()

def get_ipmi_user_number(user):
    for i in range(1, 17):
        ipmi_user_number = "User%s" % i
        (status, output) = commands.getstatusoutput('bmc-config --checkout --key-pair="%s:Username"' % ipmi_user_number)
        if user in output:
            return ipmi_user_number
    return None

def commit_ipmi_user_settings(user, password):
    ipmi_user_number = get_ipmi_user_number(user)
    if ipmi_user_number is None:
        (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="User10:Username=%s"' % user)
        ipmi_user_number = get_ipmi_user_number(user)
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Password=%s"' % (ipmi_user_number, password))
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Enable_User=Yes"' % ipmi_user_number)
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Enable_IPMI_Msgs=Yes"' % ipmi_user_number)
    (status, output) = commands.getstatusoutput('bmc-config --commit --key-pair="%s:Lan_Privilege_Limit=Administrator"' % ipmi_user_number)

def commit_ipmi_settings(config):
    (status, output) = commands.getstatusoutput('bmc-config --commit --filename %s' % config)

def get_maas_power_settings(user, password, ipaddress):
    return "%s,%s,%s" % (user, password, ipaddress)

def generate_random_password(min=8,max=15):
    length=random.randint(min,max)
    letters=string.ascii_letters+string.digits
    return ''.join([random.choice(letters) for _ in range(length)])

def main():

    import argparse

    parser = argparse.ArgumentParser(
        description='send config file to modify IPMI settings with')
    parser.add_argument("--configdir", metavar="folder",
        help="specify config file", default=None)
    parser.add_argument("--dhcp-if-static", action="store_true",
        dest="dhcp", help="specify config file", default=False)

    args = parser.parse_args()

    # Check whether IPMI exists or not.
    (status, ipmi_version) = detect_ipmi()
    if status != True:
        # if False, then failed to detect ipmi
        exit(1)

    # Check whether IPMI is being set to DHCP. If it is not, and
    # '--dhcp-if-static' has been passed,  Set it to IPMI to DHCP.
    if not is_ipmi_dhcp() and args.dhcp:
        set_ipmi_network_source("Use_DHCP")
        # allow IPMI 120 seconds to obtain an IP address
        time.sleep(120)

    # create user/pass
    IPMI_MAAS_USER="maas"
    IPMI_MAAS_PASSWORD=generate_random_password()

    # Configure IPMI user/password
    commit_ipmi_user_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD)

    # Commit other IPMI settings
    if args.configdir:
        for file in os.listdir(args.configdir):
            commit_ipmi_settings(os.path.join(args.configdir, file))

    # get the IP address
    IPMI_IP_ADDRESS = get_ipmi_ip_address()
    if IPMI_IP_ADDRESS == "0.0.0.0":
        # if IPMI_IP_ADDRESS is 0.0.0.0, wait 60 seconds and retry.
        set_ipmi_network_source("Static")
        time.sleep(2)
        set_ipmi_network_source("Use_DHCP")
        time.sleep(60)
        IPMI_IP_ADDRESS = get_ipmi_ip_address()

    if IPMI_IP_ADDRESS is None or IPMI_IP_ADDRESS == "0.0.0.0":
        # Exit (to not set power params in MAAS) if no IPMI_IP_ADDRESS
        # has been detected
        exit(1)

    print get_maas_power_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS)

if __name__ == '__main__':
    main()
END_MAAS_IPMI_AUTODETECT

add_bin "maas-signal" <<"END_MAAS_SIGNAL"
#!/usr/bin/python

from email.utils import parsedate
import mimetypes
import oauth.oauth as oauth
import os.path
import random
import string
import sys
import time
import urllib2
import yaml
import json

MD_VERSION = "2012-03-01"
VALID_STATUS = ("OK", "FAILED", "WORKING")
POWER_TYPES = ("ipmi", "virsh", "ether_wake")


def _encode_field(field_name, data, boundary):
    return ('--' + boundary,
            'Content-Disposition: form-data; name="%s"' % field_name,
            '', str(data))


def _encode_file(name, fileObj, boundary):
    return ('--' + boundary,
            'Content-Disposition: form-data; name="%s"; filename="%s"' %
                (name, name),
            'Content-Type: %s' % _get_content_type(name),
            '', fileObj.read())


def _random_string(length):
    return ''.join(random.choice(string.letters) for ii in range(length + 1))


def _get_content_type(filename):
    return mimetypes.guess_type(filename)[0] or 'application/octet-stream'


def encode_multipart_data(data, files):
    """Create a MIME multipart payload from L{data} and L{files}.

    @param data: A mapping of names (ASCII strings) to data (byte string).
    @param files: A mapping of names (ASCII strings) to file objects ready to
        be read.
    @return: A 2-tuple of C{(body, headers)}, where C{body} is a a byte string
        and C{headers} is a dict of headers to add to the enclosing request in
        which this payload will travel.
    """
    boundary = _random_string(30)

    lines = []
    for name in data:
        lines.extend(_encode_field(name, data[name], boundary))
    for name in files:
        lines.extend(_encode_file(name, files[name], boundary))
    lines.extend(('--%s--' % boundary, ''))
    body = '\r\n'.join(lines)

    headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
               'content-length': str(len(body))}

    return body, headers


def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
                  clockskew=0):
    consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
    token = oauth.OAuthToken(token_key, token_secret)

    timestamp = int(time.time()) + clockskew

    params = {
        'oauth_version': "1.0",
        'oauth_nonce': oauth.generate_nonce(),
        'oauth_timestamp': timestamp,
        'oauth_token': token.key,
        'oauth_consumer_key': consumer.key,
    }
    req = oauth.OAuthRequest(http_url=url, parameters=params)
    req.sign_request(oauth.OAuthSignatureMethod_PLAINTEXT(),
        consumer, token)
    return(req.to_header())


def geturl(url, creds, headers=None, data=None):
    # Takes a dict of creds to be passed through to oauth_headers,
    #   so it should have consumer_key, token_key, ...
    if headers is None:
        headers = {}
    else:
        headers = dict(headers)

    clockskew = 0

    def warn(msg):
        sys.stderr.write(msg + "\n")

    exc = Exception("Unexpected Error")
    for naptime in (1, 1, 2, 4, 8, 16, 32):
        if creds.get('consumer_key', None) != None:
            headers.update(oauth_headers(url,
                consumer_key=creds['consumer_key'],
                token_key=creds['token_key'],
                token_secret=creds['token_secret'],
                consumer_secret=creds['consumer_secret'],
                clockskew=clockskew))
        try:
            req = urllib2.Request(url=url, data=data, headers=headers)
            return(urllib2.urlopen(req).read())
        except urllib2.HTTPError as exc:
            if 'date' not in exc.headers:
                warn("date field not in %d headers" % exc.code)
                pass
            elif (exc.code == 401 or exc.code == 403):
                date = exc.headers['date']
                try:
                    ret_time = time.mktime(parsedate(date))
                    clockskew = int(ret_time - time.time())
                    warn("updated clock skew to %d" % clockskew)
                except:
                    warn("failed to convert date '%s'" % date)
        except Exception as exc:
            pass

        warn("request to %s failed. sleeping %d.: %s" % (url, naptime, exc))
        time.sleep(naptime)

    raise exc


def read_config(url, creds):
    if url.startswith("http://") or url.startswith("https://"):
        cfg_str = urllib2.urlopen(urllib2.Request(url=url))
    else:
        if url.startswith("file://"):
            url = url[7:]
        cfg_str = open(url,"r").read()

    cfg = yaml.safe_load(cfg_str)

    # Support reading cloud-init config for MAAS datasource.
    if 'datasource' in cfg:
        cfg = cfg['datasource']['MAAS']

    for key in creds.keys():
        if key in cfg and creds[key] == None:
            creds[key] = cfg[key]

def fail(msg):
    sys.stderr.write("FAIL: %s" % msg)
    sys.exit(1)


def main():
    """
    Call with single argument of directory or http or https url.
    If url is given additional arguments are allowed, which will be
    interpreted as consumer_key, token_key, token_secret, consumer_secret.
    """
    import argparse
    import pprint

    parser = argparse.ArgumentParser(
        description='Send signal operation and optionally post files to MAAS')
    parser.add_argument("--config", metavar="file",
        help="Specify config file", default=None)
    parser.add_argument("--ckey", metavar="key",
        help="The consumer key to auth with", default=None)
    parser.add_argument("--tkey", metavar="key",
        help="The token key to auth with", default=None)
    parser.add_argument("--csec", metavar="secret",
        help="The consumer secret (likely '')", default="")
    parser.add_argument("--tsec", metavar="secret",
        help="The token secret to auth with", default=None)
    parser.add_argument("--apiver", metavar="version",
        help="The apiver to use ("" can be used)", default=MD_VERSION)
    parser.add_argument("--url", metavar="url",
        help="The data source to query", default=None)
    parser.add_argument("--file", dest='files',
        help="File to post", action='append', default=[])
    parser.add_argument("--post", dest='posts',
        help="name=value pairs to post", action='append', default=[])
    parser.add_argument("--power-type", dest='power_type',
        help="Power type.", choices=POWER_TYPES, default=None)
    parser.add_argument("--power-parameters", dest='power_parms',
        help="Power parameters.", default=None)

    parser.add_argument("status",
        help="Status", choices=VALID_STATUS, action='store')
    parser.add_argument("message", help="Optional message",
        default="", nargs='?')

    args = parser.parse_args()

    creds = {'consumer_key': args.ckey, 'token_key': args.tkey,
        'token_secret': args.tsec, 'consumer_secret': args.csec,
        'metadata_url': args.url}

    if args.config:
        read_config(args.config, creds)

    url = creds.get('metadata_url', None)
    if not url:
        fail("URL must be provided either in --url or in config\n")
    url = "%s/%s/" % (url, args.apiver)

    params = {
        "op": "signal",
        "status": args.status,
        "error": args.message}

    for ent in args.posts:
        try:
           (key, val) = ent.split("=", 2)
        except ValueError:
           sys.stderr.write("'%s' had no '='" % ent)
           sys.exit(1)
        params[key] = val

    if args.power_parms is not None:
        params["power_type"] = args.power_type
        power_parms = dict(
            power_user=args.power_parms.split(",")[0],
            power_pass=args.power_parms.split(",")[1],
            power_address=args.power_parms.split(",")[2]
            )
        params["power_parameters"] = json.dumps(power_parms)

    files = {}
    for fpath in args.files:
        files[os.path.basename(fpath)] = open(fpath, "r")

    data, headers = encode_multipart_data(params, files)

    exc = None
    msg = ""

    try:
        payload = geturl(url, creds=creds, headers=headers, data=data)
        if payload != "OK":
            raise TypeError("Unexpected result from call: %s" % payload)
        else:
            msg = "Success"
    except urllib2.HTTPError as exc:
        msg = "http error [%s]" % exc.code
    except urllib2.URLError as exc:
        msg = "url error [%s]" % exc.reason
    except socket.timeout as exc:
        msg = "socket timeout [%s]" % exc
    except TypeError as exc:
        msg = exc.message
    except Exception as exc:
        msg = "unexpected error [%s]" % exc

    sys.stderr.write("%s\n" % msg)
    sys.exit((exc is None))

if __name__ == '__main__':
    main()
END_MAAS_SIGNAL

main
exit