~smoser/curtin/trunk.bzr-dead

« back to all changes in this revision

Viewing changes to curtin/reporter/legacy/maas.py

  • Committer: Scott Moser
  • Date: 2017-12-20 17:33:03 UTC
  • Revision ID: smoser@ubuntu.com-20171220173303-29gha5qb8wpqrd40
README: Mention move of revision control to git.

curtin development has moved its revision control to git.
It is available at
  https://code.launchpad.net/curtin

Clone with
  git clone https://git.launchpad.net/curtin
or
  git clone git+ssh://git.launchpad.net/curtin

For more information see
  http://curtin.readthedocs.io/en/latest/topics/development.html

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from curtin import url_helper
2
 
 
3
 
from . import (BaseReporter, LoadReporterException)
4
 
 
5
 
import mimetypes
6
 
import os.path
7
 
import random
8
 
import string
9
 
import sys
10
 
 
11
 
 
12
 
class MAASReporter(BaseReporter):
13
 
 
14
 
    def __init__(self, config):
15
 
        """Load config dictionary and initialize object."""
16
 
        self.url = config['url']
17
 
        self.urlhelper = url_helper.OauthUrlHelper(
18
 
            consumer_key=config.get('consumer_key'),
19
 
            token_key=config.get('token_key'),
20
 
            token_secret=config.get('token_secret'),
21
 
            consumer_secret='',
22
 
            skew_data_file="/run/oauth_skew.json")
23
 
        self.files = []
24
 
        self.retries = config.get('retries', [1, 1, 2, 4, 8, 16, 32])
25
 
 
26
 
    def report_success(self):
27
 
        """Report installation success."""
28
 
        status = "OK"
29
 
        message = "Installation succeeded."
30
 
        self.report(status, message, files=self.files)
31
 
 
32
 
    def report_failure(self, message):
33
 
        """Report installation failure."""
34
 
        status = "FAILED"
35
 
        self.report(status, message, files=self.files)
36
 
 
37
 
    def encode_multipart_data(self, data, files):
38
 
        """Create a MIME multipart payload from L{data} and L{files}.
39
 
 
40
 
        @param data: A mapping of names (ASCII strings) to data (byte string).
41
 
        @param files: A mapping of names (ASCII strings) to file objects ready
42
 
            to be read.
43
 
        @return: A 2-tuple of C{(body, headers)}, where C{body} is a a byte
44
 
            string and C{headers} is a dict of headers to add to the enclosing
45
 
            request in which this payload will travel.
46
 
        """
47
 
        boundary = self._random_string(30)
48
 
 
49
 
        lines = []
50
 
        for name in data:
51
 
            lines.extend(self._encode_field(name, data[name], boundary))
52
 
        for name in files:
53
 
            lines.extend(self._encode_file(name, files[name], boundary))
54
 
        lines.extend(('--%s--' % boundary, ''))
55
 
        body = '\r\n'.join(lines)
56
 
 
57
 
        headers = {
58
 
            'content-type': 'multipart/form-data; boundary=' + boundary,
59
 
            'content-length': "%d" % len(body),
60
 
        }
61
 
        return body, headers
62
 
 
63
 
    def report(self, status, message=None, files=None):
64
 
        """Send the report."""
65
 
 
66
 
        params = {}
67
 
        params['status'] = status
68
 
        if message is not None:
69
 
            params['error'] = message
70
 
 
71
 
        if files is None:
72
 
            files = []
73
 
        install_files = {}
74
 
        for fpath in files:
75
 
            install_files[os.path.basename(fpath)] = open(fpath, "r")
76
 
 
77
 
        data, headers = self.encode_multipart_data(params, install_files)
78
 
 
79
 
        msg = ""
80
 
 
81
 
        if not isinstance(data, bytes):
82
 
            data = data.encode()
83
 
 
84
 
        try:
85
 
            payload = self.urlhelper.geturl(
86
 
                self.url, data=data, headers=headers,
87
 
                retries=self.retries)
88
 
            if payload != b'OK':
89
 
                raise TypeError("Unexpected result from call: %s" % payload)
90
 
            else:
91
 
                msg = "Success"
92
 
        except url_helper.UrlError as exc:
93
 
            msg = str(exc)
94
 
        except Exception as exc:
95
 
            raise exc
96
 
 
97
 
        sys.stderr.write("%s\n" % msg)
98
 
 
99
 
    def _encode_field(self, field_name, data, boundary):
100
 
        return (
101
 
            '--' + boundary,
102
 
            'Content-Disposition: form-data; name="%s"' % field_name,
103
 
            '', str(data),
104
 
            )
105
 
 
106
 
    def _encode_file(self, name, fileObj, boundary):
107
 
        return (
108
 
            '--' + boundary,
109
 
            'Content-Disposition: form-data; name="%s"; filename="%s"'
110
 
            % (name, name),
111
 
            'Content-Type: %s' % self._get_content_type(name),
112
 
            '',
113
 
            fileObj.read(),
114
 
            )
115
 
 
116
 
    def _random_string(self, length):
117
 
        return ''.join(random.choice(string.ascii_letters)
118
 
                       for ii in range(length + 1))
119
 
 
120
 
    def _get_content_type(self, filename):
121
 
        return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
122
 
 
123
 
 
124
 
def load_factory(options):
125
 
    try:
126
 
        return MAASReporter(options)
127
 
    except Exception:
128
 
        raise LoadReporterException