12
from maas_api_helper import (
18
MD_VERSION = "2012-03-01"
19
VALID_STATUS = ("OK", "FAILED", "WORKING")
20
POWER_TYPES = ("ipmi", "virsh", "ether_wake")
23
def _encode_field(field_name, data, boundary):
24
return ('--' + boundary,
25
'Content-Disposition: form-data; name="%s"' % field_name,
29
def _encode_file(name, fileObj, boundary):
30
return ('--' + boundary,
31
'Content-Disposition: form-data; name="%s"; filename="%s"' %
33
'Content-Type: %s' % _get_content_type(name),
37
def _random_string(length):
38
return ''.join(random.choice(string.letters) for ii in range(length + 1))
41
def _get_content_type(filename):
42
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
45
def encode_multipart_data(data, files):
46
"""Create a MIME multipart payload from L{data} and L{files}.
48
@param data: A mapping of names (ASCII strings) to data (byte string).
49
@param files: A mapping of names (ASCII strings) to file objects ready to
51
@return: A 2-tuple of C{(body, headers)}, where C{body} is a a byte string
52
and C{headers} is a dict of headers to add to the enclosing request in
53
which this payload will travel.
55
boundary = _random_string(30)
59
lines.extend(_encode_field(name, data[name], boundary))
61
lines.extend(_encode_file(name, files[name], boundary))
62
lines.extend(('--%s--' % boundary, ''))
63
body = '\r\n'.join(lines)
65
headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
66
'content-length': str(len(body))}
72
sys.stderr.write("FAIL: %s" % msg)
78
Call with single argument of directory or http or https url.
79
If url is given additional arguments are allowed, which will be
80
interpreted as consumer_key, token_key, token_secret, consumer_secret.
84
parser = argparse.ArgumentParser(
85
description='Send signal operation and optionally post files to MAAS')
86
parser.add_argument("--config", metavar="file",
87
help="Specify config file", default=None)
88
parser.add_argument("--ckey", metavar="key",
89
help="The consumer key to auth with", default=None)
90
parser.add_argument("--tkey", metavar="key",
91
help="The token key to auth with", default=None)
92
parser.add_argument("--csec", metavar="secret",
93
help="The consumer secret (likely '')", default="")
94
parser.add_argument("--tsec", metavar="secret",
95
help="The token secret to auth with", default=None)
96
parser.add_argument("--apiver", metavar="version",
97
help="The apiver to use ("" can be used)", default=MD_VERSION)
98
parser.add_argument("--url", metavar="url",
99
help="The data source to query", default=None)
100
parser.add_argument("--file", dest='files',
101
help="File to post", action='append', default=[])
102
parser.add_argument("--post", dest='posts',
103
help="name=value pairs to post", action='append', default=[])
104
parser.add_argument("--power-type", dest='power_type',
105
help="Power type.", choices=POWER_TYPES, default=None)
106
parser.add_argument("--power-parameters", dest='power_parms',
107
help="Power parameters.", default=None)
109
"--script-result", metavar="retval", type=int, dest='script_result',
110
help="Return code of a commissioning script.")
112
parser.add_argument("status",
113
help="Status", choices=VALID_STATUS, action='store')
114
parser.add_argument("message", help="Optional message",
115
default="", nargs='?')
117
args = parser.parse_args()
120
'consumer_key': args.ckey,
121
'token_key': args.tkey,
122
'token_secret': args.tsec,
123
'consumer_secret': args.csec,
124
'metadata_url': args.url,
128
read_config(args.config, creds)
130
url = creds.get('metadata_url', None)
132
fail("URL must be provided either in --url or in config\n")
133
url = "%s/%s/" % (url, args.apiver)
137
"status": args.status,
138
"error": args.message,
141
if args.script_result is not None:
142
params['script_result'] = args.script_result
144
for ent in args.posts:
146
(key, val) = ent.split("=", 2)
148
sys.stderr.write("'%s' had no '='" % ent)
152
if args.power_parms is not None:
153
params["power_type"] = args.power_type
155
power_user=args.power_parms.split(",")[0],
156
power_pass=args.power_parms.split(",")[1],
157
power_address=args.power_parms.split(",")[2]
159
params["power_parameters"] = json.dumps(power_parms)
162
for fpath in args.files:
163
files[os.path.basename(fpath)] = open(fpath, "r")
165
data, headers = encode_multipart_data(params, files)
171
payload = geturl(url, creds=creds, headers=headers, data=data)
173
raise TypeError("Unexpected result from call: %s" % payload)
176
except urllib2.HTTPError as exc:
177
msg = "http error [%s]" % exc.code
178
except urllib2.URLError as exc:
179
msg = "url error [%s]" % exc.reason
180
except socket.timeout as exc:
181
msg = "socket timeout [%s]" % exc
182
except TypeError as exc:
184
except Exception as exc:
185
msg = "unexpected error [%s]" % exc
187
sys.stderr.write("%s\n" % msg)
188
sys.exit((exc is None))
190
if __name__ == '__main__':