Beispiel #1
0
    def _cs_request(self, url, method, **kwargs):
        # Check that certain things are called correctly
        if method in ['GET', 'DELETE']:
            assert 'body' not in kwargs
        elif method == 'PUT':
            assert 'body' in kwargs

        # Call the method
        args = urlutils.parse_qsl(urlutils.urlparse(url)[4])
        kwargs.update(args)
        munged_url = url.rsplit('?', 1)[0]
        munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_')
        munged_url = munged_url.replace('-', '_')

        callback = "%s_%s" % (method.lower(), munged_url)

        if not hasattr(self, callback):
            raise AssertionError('Called unknown API method: %s %s, '
                                 'expected fakes method name: %s' %
                                 (method, url, callback))

        # Note the call
        self.callstack.append((method, url, kwargs.get('body', None)))

        status, body = getattr(self, callback)(**kwargs)
        if hasattr(status, 'items'):
            return httplib2.Response(status), body
        else:
            return httplib2.Response({"status": status}), body
Beispiel #2
0
 def FnGetAtt(self, key):
     url, token_id = self.swift().get_auth()
     parsed = list(urlutils.urlparse(url))
     if key == 'DomainName':
         return parsed[1].split(':')[0]
     elif key == 'WebsiteURL':
         return '%s://%s%s/%s' % (parsed[0], parsed[1], parsed[2],
                                  self.resource_id)
     elif key == 'RootURL':
         return '%s://%s%s' % (parsed[0], parsed[1], parsed[2])
     elif self.resource_id and key in (
             'ObjectCount', 'BytesUsed', 'HeadContainer'):
         try:
             headers = self.swift().head_container(self.resource_id)
         except clients.swiftclient.ClientException as ex:
             logger.warn(_("Head container failed: %s") % str(ex))
             return None
         else:
             if key == 'ObjectCount':
                 return headers['x-container-object-count']
             elif key == 'BytesUsed':
                 return headers['x-container-bytes-used']
             elif key == 'HeadContainer':
                 return headers
     else:
         raise exception.InvalidTemplateAttribute(resource=self.name,
                                                  key=key)
Beispiel #3
0
    def _cs_request(self, url, method, **kwargs):
        # Check that certain things are called correctly
        if method in ['GET', 'DELETE']:
            assert 'body' not in kwargs
        elif method == 'PUT':
            assert 'body' in kwargs

        # Call the method
        args = urlutils.parse_qsl(urlutils.urlparse(url)[4])
        kwargs.update(args)
        munged_url = url.rsplit('?', 1)[0]
        munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_')
        munged_url = munged_url.replace('-', '_')

        callback = "%s_%s" % (method.lower(), munged_url)

        if not hasattr(self, callback):
            raise AssertionError('Called unknown API method: %s %s, '
                                 'expected fakes method name: %s' %
                                 (method, url, callback))

        # Note the call
        self.callstack.append((method, url, kwargs.get('body', None)))

        status, body = getattr(self, callback)(**kwargs)
        if hasattr(status, 'items'):
            return httplib2.Response(status), body
        else:
            return httplib2.Response({"status": status}), body
    def _load_mysql_dump_file(self, engine, file_name):
        for key, eng in self.engines.items():
            if eng is engine:
                conn_string = self.test_databases[key]
                conn_pieces = urlutils.urlparse(conn_string)
                if conn_string.startswith('mysql'):
                    break
                else:
                    return

        (user, password, database, host) = \
            test_migrations.get_db_connection_info(conn_pieces)
        cmd = (
            'mysql -u \"%(user)s\" -p\"%(password)s\" -h %(host)s %(db)s ') % {
                'user': user,
                'password': password,
                'host': host,
                'db': database
            }
        file_path = os.path.join(os.path.dirname(__file__), file_name)
        with open(file_path) as sql_file:
            process = subprocess.Popen(cmd,
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stdin=sql_file,
                                       stderr=subprocess.STDOUT)
            output = process.communicate()[0]
            self.assertEqual(0, process.returncode,
                             "Failed to run: %s\n%s" % (cmd, output))
Beispiel #5
0
 def _reset_databases(self):
     for key, engine in self.engines.items():
         conn_string = self.test_databases[key]
         conn_pieces = urlutils.urlparse(conn_string)
         engine.dispose()
         if conn_string.startswith('sqlite'):
             # We can just delete the SQLite database, which is
             # the easiest and cleanest solution
             db_path = conn_pieces.path.strip('/')
             if os.path.exists(db_path):
                 os.unlink(db_path)
             # No need to recreate the SQLite DB. SQLite will
             # create it for us if it's not there...
         elif conn_string.startswith('mysql'):
             # We can execute the MySQL client to destroy and re-create
             # the MYSQL database, which is easier and less error-prone
             # than using SQLAlchemy to do this via MetaData...trust me.
             (user, password, database, host) = \
                 get_db_connection_info(conn_pieces)
             sql = ("drop database if exists %(db)s; "
                    "create database %(db)s;") % {'db': database}
             cmd = ("mysql -u \"%(user)s\" -p\"%(password)s\" -h %(host)s "
                    "-e \"%(sql)s\"") % {'user': user, 'password': password,
                                         'host': host, 'sql': sql}
             self.execute_cmd(cmd)
         elif conn_string.startswith('postgresql'):
             self._reset_pg(conn_pieces)
    def _load_mysql_dump_file(self, engine, file_name):
        for key, eng in self.engines.items():
            if eng is engine:
                conn_string = self.test_databases[key]
                conn_pieces = urlutils.urlparse(conn_string)
                if conn_string.startswith('mysql'):
                    break
                else:
                    return

        (user, password, database, host) = \
            test_migrations.get_db_connection_info(conn_pieces)
        cmd = ('mysql -u \"%(user)s\" -p\"%(password)s\" -h %(host)s %(db)s '
               ) % {'user': user, 'password': password,
                    'host': host, 'db': database}
        file_path = os.path.join(os.path.dirname(__file__),
                                 file_name)
        with open(file_path) as sql_file:
            process = subprocess.Popen(cmd, shell=True,
                                       stdout=subprocess.PIPE,
                                       stdin=sql_file,
                                       stderr=subprocess.STDOUT)
            output = process.communicate()[0]
            self.assertEqual(0, process.returncode,
                             "Failed to run: %s\n%s" % (cmd, output))
Beispiel #7
0
 def _reset_databases(self):
     for key, engine in self.engines.items():
         conn_string = self.test_databases[key]
         conn_pieces = urlutils.urlparse(conn_string)
         engine.dispose()
         if conn_string.startswith('sqlite'):
             # We can just delete the SQLite database, which is
             # the easiest and cleanest solution
             db_path = conn_pieces.path.strip('/')
             if os.path.exists(db_path):
                 os.unlink(db_path)
             # No need to recreate the SQLite DB. SQLite will
             # create it for us if it's not there...
         elif conn_string.startswith('mysql'):
             # We can execute the MySQL client to destroy and re-create
             # the MYSQL database, which is easier and less error-prone
             # than using SQLAlchemy to do this via MetaData...trust me.
             (user, password, database, host) = \
                 get_db_connection_info(conn_pieces)
             sql = ("drop database if exists %(db)s; "
                    "create database %(db)s;") % {'db': database}
             cmd = ("mysql -u \"%(user)s\" -p\"%(password)s\" -h %(host)s "
                    "-e \"%(sql)s\"") % {'user': user, 'password': password,
                                         'host': host, 'sql': sql}
             self.execute_cmd(cmd)
         elif conn_string.startswith('postgresql'):
             self._reset_pg(conn_pieces)
Beispiel #8
0
 def _resolve_attribute(self, name):
     url = self.swift().get_auth()[0]
     parsed = list(urlutils.urlparse(url))
     if name == 'DomainName':
         return parsed[1].split(':')[0]
     elif name == 'WebsiteURL':
         return '%s://%s%s/%s' % (parsed[0], parsed[1], parsed[2],
                                  self.resource_id)
Beispiel #9
0
 def _resolve_attribute(self, name):
     url = self.swift().get_auth()[0]
     parsed = list(urlutils.urlparse(url))
     if name == 'DomainName':
         return parsed[1].split(':')[0]
     elif name == 'WebsiteURL':
         return '%s://%s%s/%s' % (parsed[0], parsed[1], parsed[2],
                                  self.resource_id)
Beispiel #10
0
    def _get_signed_url(self, signal_type=SIGNAL):
        """Create properly formatted and pre-signed URL.

        This uses the created user for the credentials.

        See boto/auth.py::QuerySignatureV2AuthHandler

        :param signal_type: either WAITCONDITION or SIGNAL.
        """
        try:
            stored = db_api.resource_data_get(self, 'ec2_signed_url')
        except exception.NotFound:
            stored = None
        if stored is not None:
            return stored

        try:
            access_key = db_api.resource_data_get(self, 'access_key')
            secret_key = db_api.resource_data_get(self, 'secret_key')
        except exception.NotFound:
            logger.warning(
                _('Cannot generate signed url, '
                  'no stored access/secret key'))
            return

        waitcond_url = cfg.CONF.heat_waitcondition_server_url
        signal_url = waitcond_url.replace('/waitcondition', signal_type)
        host_url = urlutils.urlparse(signal_url)

        path = self.identifier().arn_url_path()

        # Note the WSGI spec apparently means that the webob request we end up
        # prcessing in the CFN API (ec2token.py) has an unquoted path, so we
        # need to calculate the signature with the path component unquoted, but
        # ensure the actual URL contains the quoted version...
        unquoted_path = urlutils.unquote(host_url.path + path)
        request = {
            'host': host_url.netloc.lower(),
            'verb': SIGNAL_VERB[signal_type],
            'path': unquoted_path,
            'params': {
                'SignatureMethod': 'HmacSHA256',
                'SignatureVersion': '2',
                'AWSAccessKeyId': access_key,
                'Timestamp': self.created_time.strftime("%Y-%m-%dT%H:%M:%SZ")
            }
        }
        # Sign the request
        signer = ec2_utils.Ec2Signer(secret_key)
        request['params']['Signature'] = signer.generate(request)

        qs = urlutils.urlencode(request['params'])
        url = "%s%s?%s" % (signal_url.lower(), path, qs)

        db_api.resource_data_set(self, 'ec2_signed_url', url)
        return url
    def _get_signed_url(self, signal_type=SIGNAL):
        """Create properly formatted and pre-signed URL.

        This uses the created user for the credentials.

        See boto/auth.py::QuerySignatureV2AuthHandler

        :param signal_type: either WAITCONDITION or SIGNAL.
        """
        try:
            stored = db_api.resource_data_get(self, 'ec2_signed_url')
        except exception.NotFound:
            stored = None
        if stored is not None:
            return stored

        try:
            access_key = db_api.resource_data_get(self, 'access_key')
            secret_key = db_api.resource_data_get(self, 'secret_key')
        except exception.NotFound:
            logger.warning(_('Cannot generate signed url, '
                             'no stored access/secret key'))
            return

        waitcond_url = cfg.CONF.heat_waitcondition_server_url
        signal_url = waitcond_url.replace('/waitcondition', signal_type)
        host_url = urlutils.urlparse(signal_url)

        path = self.identifier().arn_url_path()

        # Note the WSGI spec apparently means that the webob request we end up
        # prcessing in the CFN API (ec2token.py) has an unquoted path, so we
        # need to calculate the signature with the path component unquoted, but
        # ensure the actual URL contains the quoted version...
        unquoted_path = urlutils.unquote(host_url.path + path)
        request = {'host': host_url.netloc.lower(),
                   'verb': SIGNAL_VERB[signal_type],
                   'path': unquoted_path,
                   'params': {'SignatureMethod': 'HmacSHA256',
                              'SignatureVersion': '2',
                              'AWSAccessKeyId': access_key,
                              'Timestamp':
                              self.created_time.strftime("%Y-%m-%dT%H:%M:%SZ")
                              }}
        # Sign the request
        signer = ec2_utils.Ec2Signer(secret_key)
        request['params']['Signature'] = signer.generate(request)

        qs = urlutils.urlencode(request['params'])
        url = "%s%s?%s" % (signal_url.lower(),
                           path, qs)

        db_api.resource_data_set(self, 'ec2_signed_url', url)
        return url
    def test_get_collection_links_does_not_overwrite_other_params(self):
        self.setUpGetCollectionLinks()
        self.request.params = {'limit': '2', 'foo': 'bar'}
        links = views_common.get_collection_links(self.request, self.items)

        next_link = filter(lambda link: link['rel'] == 'next', links).pop()
        url = next_link['href']
        query_string = urlutils.urlparse(url).query
        params = {}
        params.update(urlutils.parse_qsl(query_string))
        self.assertEqual('2', params['limit'])
        self.assertEqual('bar', params['foo'])
    def test_get_collection_links_does_not_overwrite_other_params(self):
        self.setUpGetCollectionLinks()
        self.request.params = {'limit': '2', 'foo': 'bar'}
        links = views_common.get_collection_links(self.request, self.items)

        next_link = filter(lambda link: link['rel'] == 'next', links).pop()
        url = next_link['href']
        query_string = urlutils.urlparse(url).query
        params = {}
        params.update(urlutils.parse_qsl(query_string))
        self.assertEqual('2', params['limit'])
        self.assertEqual('bar', params['foo'])
Beispiel #14
0
def get(url, allowed_schemes=('http', 'https')):
    '''
    Get the data at the specifier URL.

    The URL must use the http: or https: schemes.
    The file: scheme is also supported if you override
    the allowed_schemes argument.
    Raise an IOError if getting the data fails.
    '''
    logger.info(_('Fetching data from %s') % url)

    components = urlutils.urlparse(url)

    if components.scheme not in allowed_schemes:
        raise IOError(_('Invalid URL scheme %s') % components.scheme)

    if components.scheme == 'file':
        try:
            return urlutils.urlopen(url).read()
        except urlutils.URLError as uex:
            raise IOError(_('Failed to retrieve template: %s') % str(uex))

    try:
        resp = requests.get(url, stream=True)
        resp.raise_for_status()

        # We cannot use resp.text here because it would download the
        # entire file, and a large enough file would bring down the
        # engine.  The 'Content-Length' header could be faked, so it's
        # necessary to download the content in chunks to until
        # max_template_size is reached.  The chunk_size we use needs
        # to balance CPU-intensive string concatenation with accuracy
        # (eg. it's possible to fetch 1000 bytes greater than
        # max_template_size with a chunk_size of 1000).
        reader = resp.iter_content(chunk_size=1000)
        result = ""
        for chunk in reader:
            result += chunk
            if len(result) > cfg.CONF.max_template_size:
                raise IOError("Template exceeds maximum allowed size (%s "
                              "bytes)" % cfg.CONF.max_template_size)
        return result

    except exceptions.RequestException as ex:
        raise IOError(_('Failed to retrieve template: %s') % str(ex))
Beispiel #15
0
    def from_arn_url(cls, url):
        """
        Return a new HeatIdentifier generated by parsing the supplied URL
        The URL is expected to contain a valid arn as part of the path
        """
        # Sanity check the URL
        urlp = urlutils.urlparse(url)
        if urlp.scheme not in ("http", "https") or not urlp.netloc or not urlp.path:
            raise ValueError(_('"%s" is not a valid URL') % url)

        # Remove any query-string and extract the ARN
        arn_url_prefix = "/arn%3Aopenstack%3Aheat%3A%3A"
        match = re.search(arn_url_prefix, urlp.path, re.IGNORECASE)
        if match is None:
            raise ValueError(_('"%s" is not a valid ARN URL') % url)
        # the +1 is to skip the leading /
        url_arn = urlp.path[match.start() + 1 :]
        arn = urlutils.unquote(url_arn)
        return cls.from_arn(arn)
Beispiel #16
0
    def from_arn_url(cls, url):
        '''
        Return a new HeatIdentifier generated by parsing the supplied URL
        The URL is expected to contain a valid arn as part of the path
        '''
        # Sanity check the URL
        urlp = urlutils.urlparse(url)
        if (urlp.scheme not in ('http', 'https') or not urlp.netloc
                or not urlp.path):
            raise ValueError(_('"%s" is not a valid URL') % url)

        # Remove any query-string and extract the ARN
        arn_url_prefix = '/arn%3Aopenstack%3Aheat%3A%3A'
        match = re.search(arn_url_prefix, urlp.path, re.IGNORECASE)
        if match is None:
            raise ValueError(_('"%s" is not a valid ARN URL') % url)
        # the +1 is to skip the leading /
        url_arn = urlp.path[match.start() + 1:]
        arn = urlutils.unquote(url_arn)
        return cls.from_arn(arn)
Beispiel #17
0
def build_userdata(resource, userdata=None, instance_user=None,
                   user_data_format='HEAT_CFNTOOLS'):
    '''
    Build multipart data blob for CloudInit which includes user-supplied
    Metadata, user data, and the required Heat in-instance configuration.

    :param resource: the resource implementation
    :type resource: heat.engine.Resource
    :param userdata: user data string
    :type userdata: str or None
    :param instance_user: the user to create on the server
    :type instance_user: string
    :param user_data_format: Format of user data to return
    :type user_data_format: string
    :returns: multipart mime as a string
    '''

    if user_data_format == 'RAW':
        return userdata

    is_cfntools = user_data_format == 'HEAT_CFNTOOLS'
    is_software_config = user_data_format == 'SOFTWARE_CONFIG'

    def make_subpart(content, filename, subtype=None):
        if subtype is None:
            subtype = os.path.splitext(filename)[0]
        msg = MIMEText(content, _subtype=subtype)
        msg.add_header('Content-Disposition', 'attachment',
                       filename=filename)
        return msg

    def read_cloudinit_file(fn):
        return pkgutil.get_data('heat', 'cloudinit/%s' % fn)

    if instance_user:
        config_custom_user = '******' % instance_user
        # FIXME(shadower): compatibility workaround for cloud-init 0.6.3. We
        # can drop this once we stop supporting 0.6.3 (which ships with Ubuntu
        # 12.04 LTS).
        #
        # See bug https://bugs.launchpad.net/heat/+bug/1257410
        boothook_custom_user = r"""useradd -m %s
echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
""" % (instance_user, instance_user)
    else:
        config_custom_user = ''
        boothook_custom_user = ''

    cloudinit_config = string.Template(
        read_cloudinit_file('config')).safe_substitute(
            add_custom_user=config_custom_user)
    cloudinit_boothook = string.Template(
        read_cloudinit_file('boothook.sh')).safe_substitute(
            add_custom_user=boothook_custom_user)

    attachments = [(cloudinit_config, 'cloud-config'),
                   (cloudinit_boothook, 'boothook.sh', 'cloud-boothook'),
                   (read_cloudinit_file('part_handler.py'),
                    'part-handler.py')]

    if is_cfntools:
        attachments.append((userdata, 'cfn-userdata', 'x-cfninitdata'))
    elif is_software_config:
        # attempt to parse userdata as a multipart message, and if it
        # is, add each part as an attachment
        userdata_parts = None
        try:
            userdata_parts = email.message_from_string(userdata)
        except:
            pass
        if userdata_parts and userdata_parts.is_multipart():
            for part in userdata_parts.get_payload():
                attachments.append((part.get_payload(),
                                    part.get_filename(),
                                    part.get_content_subtype()))
        else:
            attachments.append((userdata, 'userdata', 'x-shellscript'))

    if is_cfntools:
        attachments.append((read_cloudinit_file('loguserdata.py'),
                           'loguserdata.py', 'x-shellscript'))

    metadata = resource.metadata
    if metadata:
        attachments.append((json.dumps(metadata),
                            'cfn-init-data', 'x-cfninitdata'))

    attachments.append((cfg.CONF.heat_watch_server_url,
                        'cfn-watch-server', 'x-cfninitdata'))

    if is_cfntools:
        attachments.append((cfg.CONF.heat_metadata_server_url,
                            'cfn-metadata-server', 'x-cfninitdata'))

        # Create a boto config which the cfntools on the host use to know
        # where the cfn and cw API's are to be accessed
        cfn_url = urlutils.urlparse(cfg.CONF.heat_metadata_server_url)
        cw_url = urlutils.urlparse(cfg.CONF.heat_watch_server_url)
        is_secure = cfg.CONF.instance_connection_is_secure
        vcerts = cfg.CONF.instance_connection_https_validate_certificates
        boto_cfg = "\n".join(["[Boto]",
                              "debug = 0",
                              "is_secure = %s" % is_secure,
                              "https_validate_certificates = %s" % vcerts,
                              "cfn_region_name = heat",
                              "cfn_region_endpoint = %s" %
                              cfn_url.hostname,
                              "cloudwatch_region_name = heat",
                              "cloudwatch_region_endpoint = %s" %
                              cw_url.hostname])
        attachments.append((boto_cfg,
                            'cfn-boto-cfg', 'x-cfninitdata'))

    subparts = [make_subpart(*args) for args in attachments]
    mime_blob = MIMEMultipart(_subparts=subparts)

    return mime_blob.as_string()
Beispiel #18
0
 def __init__(self, url):
     self.url = urlutils.urlparse(url)
Beispiel #19
0
def build_userdata(resource,
                   userdata=None,
                   instance_user=None,
                   user_data_format='HEAT_CFNTOOLS'):
    '''
    Build multipart data blob for CloudInit which includes user-supplied
    Metadata, user data, and the required Heat in-instance configuration.

    :param resource: the resource implementation
    :type resource: heat.engine.Resource
    :param userdata: user data string
    :type userdata: str or None
    :param instance_user: the user to create on the server
    :type instance_user: string
    :param user_data_format: Format of user data to return
    :type user_data_format: string
    :returns: multipart mime as a string
    '''

    if user_data_format == 'RAW':
        return userdata

    is_cfntools = user_data_format == 'HEAT_CFNTOOLS'
    is_software_config = user_data_format == 'SOFTWARE_CONFIG'

    def make_subpart(content, filename, subtype=None):
        if subtype is None:
            subtype = os.path.splitext(filename)[0]
        msg = MIMEText(content, _subtype=subtype)
        msg.add_header('Content-Disposition', 'attachment', filename=filename)
        return msg

    def read_cloudinit_file(fn):
        return pkgutil.get_data('heat', 'cloudinit/%s' % fn)

    if instance_user:
        config_custom_user = '******' % instance_user
        # FIXME(shadower): compatibility workaround for cloud-init 0.6.3. We
        # can drop this once we stop supporting 0.6.3 (which ships with Ubuntu
        # 12.04 LTS).
        #
        # See bug https://bugs.launchpad.net/heat/+bug/1257410
        boothook_custom_user = r"""useradd -m %s
echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
""" % (instance_user, instance_user)
    else:
        config_custom_user = ''
        boothook_custom_user = ''

    cloudinit_config = string.Template(
        read_cloudinit_file('config')).safe_substitute(
            add_custom_user=config_custom_user)
    cloudinit_boothook = string.Template(
        read_cloudinit_file('boothook.sh')).safe_substitute(
            add_custom_user=boothook_custom_user)

    attachments = [(cloudinit_config, 'cloud-config'),
                   (cloudinit_boothook, 'boothook.sh', 'cloud-boothook'),
                   (read_cloudinit_file('part_handler.py'), 'part-handler.py')]

    if is_cfntools:
        attachments.append((userdata, 'cfn-userdata', 'x-cfninitdata'))
    elif is_software_config:
        # attempt to parse userdata as a multipart message, and if it
        # is, add each part as an attachment
        userdata_parts = None
        try:
            userdata_parts = email.message_from_string(userdata)
        except:
            pass
        if userdata_parts and userdata_parts.is_multipart():
            for part in userdata_parts.get_payload():
                attachments.append((part.get_payload(), part.get_filename(),
                                    part.get_content_subtype()))
        else:
            attachments.append((userdata, 'userdata', 'x-shellscript'))

    if is_cfntools:
        attachments.append((read_cloudinit_file('loguserdata.py'),
                            'loguserdata.py', 'x-shellscript'))

    metadata = resource.metadata
    if metadata:
        attachments.append(
            (json.dumps(metadata), 'cfn-init-data', 'x-cfninitdata'))

    attachments.append(
        (cfg.CONF.heat_watch_server_url, 'cfn-watch-server', 'x-cfninitdata'))

    if is_cfntools:
        attachments.append((cfg.CONF.heat_metadata_server_url,
                            'cfn-metadata-server', 'x-cfninitdata'))

        # Create a boto config which the cfntools on the host use to know
        # where the cfn and cw API's are to be accessed
        cfn_url = urlutils.urlparse(cfg.CONF.heat_metadata_server_url)
        cw_url = urlutils.urlparse(cfg.CONF.heat_watch_server_url)
        is_secure = cfg.CONF.instance_connection_is_secure
        vcerts = cfg.CONF.instance_connection_https_validate_certificates
        boto_cfg = "\n".join([
            "[Boto]", "debug = 0",
            "is_secure = %s" % is_secure,
            "https_validate_certificates = %s" % vcerts,
            "cfn_region_name = heat",
            "cfn_region_endpoint = %s" % cfn_url.hostname,
            "cloudwatch_region_name = heat",
            "cloudwatch_region_endpoint = %s" % cw_url.hostname
        ])
        attachments.append((boto_cfg, 'cfn-boto-cfg', 'x-cfninitdata'))

    subparts = [make_subpart(*args) for args in attachments]
    mime_blob = MIMEMultipart(_subparts=subparts)

    return mime_blob.as_string()
Beispiel #20
0
def build_userdata(resource,
                   userdata=None,
                   instance_user=None,
                   user_data_format='HEAT_CFNTOOLS'):
    '''
    Build multipart data blob for CloudInit which includes user-supplied
    Metadata, user data, and the required Heat in-instance configuration.

    :param resource: the resource implementation
    :type resource: heat.engine.Resource
    :param userdata: user data string
    :type userdata: str or None
    :param instance_user: the user to create on the server
    :type instance_user: string
    :param user_data_format: Format of user data to return
    :type user_data_format: string
    :returns: multipart mime as a string
    '''

    if user_data_format == 'RAW':
        return userdata

    def make_subpart(content, filename, subtype=None):
        if subtype is None:
            subtype = os.path.splitext(filename)[0]
        msg = MIMEText(content, _subtype=subtype)
        msg.add_header('Content-Disposition', 'attachment', filename=filename)
        return msg

    def read_cloudinit_file(fn):
        data = pkgutil.get_data('heat', 'cloudinit/%s' % fn)
        data = data.replace('@INSTANCE_USER@', instance_user
                            or cfg.CONF.instance_user)
        return data

    attachments = [(read_cloudinit_file('config'), 'cloud-config'),
                   (read_cloudinit_file('boothook.sh'), 'boothook.sh',
                    'cloud-boothook'),
                   (read_cloudinit_file('part_handler.py'), 'part-handler.py'),
                   (userdata, 'cfn-userdata', 'x-cfninitdata'),
                   (read_cloudinit_file('loguserdata.py'), 'loguserdata.py',
                    'x-shellscript')]

    if 'Metadata' in resource.t:
        attachments.append(
            (json.dumps(resource.metadata), 'cfn-init-data', 'x-cfninitdata'))

    attachments.append(
        (cfg.CONF.heat_watch_server_url, 'cfn-watch-server', 'x-cfninitdata'))

    attachments.append((cfg.CONF.heat_metadata_server_url,
                        'cfn-metadata-server', 'x-cfninitdata'))

    # Create a boto config which the cfntools on the host use to know
    # where the cfn and cw API's are to be accessed
    cfn_url = urlutils.urlparse(cfg.CONF.heat_metadata_server_url)
    cw_url = urlutils.urlparse(cfg.CONF.heat_watch_server_url)
    is_secure = cfg.CONF.instance_connection_is_secure
    vcerts = cfg.CONF.instance_connection_https_validate_certificates
    boto_cfg = "\n".join([
        "[Boto]", "debug = 0",
        "is_secure = %s" % is_secure,
        "https_validate_certificates = %s" % vcerts, "cfn_region_name = heat",
        "cfn_region_endpoint = %s" % cfn_url.hostname,
        "cloudwatch_region_name = heat",
        "cloudwatch_region_endpoint = %s" % cw_url.hostname
    ])
    attachments.append((boto_cfg, 'cfn-boto-cfg', 'x-cfninitdata'))

    subparts = [make_subpart(*args) for args in attachments]
    mime_blob = MIMEMultipart(_subparts=subparts)

    return mime_blob.as_string()
Beispiel #21
0
def build_userdata(resource, userdata=None, instance_user=None, user_data_format="HEAT_CFNTOOLS"):
    """
    Build multipart data blob for CloudInit which includes user-supplied
    Metadata, user data, and the required Heat in-instance configuration.

    :param resource: the resource implementation
    :type resource: heat.engine.Resource
    :param userdata: user data string
    :type userdata: str or None
    :param instance_user: the user to create on the server
    :type instance_user: string
    :param user_data_format: Format of user data to return
    :type user_data_format: string
    :returns: multipart mime as a string
    """

    if user_data_format == "RAW":
        return userdata

    def make_subpart(content, filename, subtype=None):
        if subtype is None:
            subtype = os.path.splitext(filename)[0]
        msg = MIMEText(content, _subtype=subtype)
        msg.add_header("Content-Disposition", "attachment", filename=filename)
        return msg

    def read_cloudinit_file(fn):
        data = pkgutil.get_data("heat", "cloudinit/%s" % fn)
        data = data.replace("@INSTANCE_USER@", instance_user or cfg.CONF.instance_user)
        return data

    attachments = [
        (read_cloudinit_file("config"), "cloud-config"),
        (read_cloudinit_file("boothook.sh"), "boothook.sh", "cloud-boothook"),
        (read_cloudinit_file("part_handler.py"), "part-handler.py"),
        (userdata, "cfn-userdata", "x-cfninitdata"),
        (read_cloudinit_file("loguserdata.py"), "loguserdata.py", "x-shellscript"),
    ]

    if "Metadata" in resource.t:
        attachments.append((json.dumps(resource.metadata), "cfn-init-data", "x-cfninitdata"))

    attachments.append((cfg.CONF.heat_watch_server_url, "cfn-watch-server", "x-cfninitdata"))

    attachments.append((cfg.CONF.heat_metadata_server_url, "cfn-metadata-server", "x-cfninitdata"))

    # Create a boto config which the cfntools on the host use to know
    # where the cfn and cw API's are to be accessed
    cfn_url = urlutils.urlparse(cfg.CONF.heat_metadata_server_url)
    cw_url = urlutils.urlparse(cfg.CONF.heat_watch_server_url)
    is_secure = cfg.CONF.instance_connection_is_secure
    vcerts = cfg.CONF.instance_connection_https_validate_certificates
    boto_cfg = "\n".join(
        [
            "[Boto]",
            "debug = 0",
            "is_secure = %s" % is_secure,
            "https_validate_certificates = %s" % vcerts,
            "cfn_region_name = heat",
            "cfn_region_endpoint = %s" % cfn_url.hostname,
            "cloudwatch_region_name = heat",
            "cloudwatch_region_endpoint = %s" % cw_url.hostname,
        ]
    )
    attachments.append((boto_cfg, "cfn-boto-cfg", "x-cfninitdata"))

    subparts = [make_subpart(*args) for args in attachments]
    mime_blob = MIMEMultipart(_subparts=subparts)

    return mime_blob.as_string()
Beispiel #22
0
def build_userdata(resource, userdata=None, instance_user=None,
                   user_data_format='HEAT_CFNTOOLS'):
    '''
    Build multipart data blob for CloudInit which includes user-supplied
    Metadata, user data, and the required Heat in-instance configuration.

    :param resource: the resource implementation
    :type resource: heat.engine.Resource
    :param userdata: user data string
    :type userdata: str or None
    :param instance_user: the user to create on the server
    :type instance_user: string
    :param user_data_format: Format of user data to return
    :type user_data_format: string
    :returns: multipart mime as a string
    '''

    if user_data_format == 'RAW':
        return userdata

    is_cfntools = user_data_format == 'HEAT_CFNTOOLS'
    is_software_config = user_data_format == 'SOFTWARE_CONFIG'

    def make_subpart(content, filename, subtype=None):
        if subtype is None:
            subtype = os.path.splitext(filename)[0]
        msg = MIMEText(content, _subtype=subtype)
        msg.add_header('Content-Disposition', 'attachment',
                       filename=filename)
        return msg

    def read_cloudinit_file(fn):
        data = pkgutil.get_data('heat', 'cloudinit/%s' % fn)
        data = data.replace('@INSTANCE_USER@',
                            instance_user or cfg.CONF.instance_user)
        return data

    attachments = [(read_cloudinit_file('config'), 'cloud-config'),
                   (read_cloudinit_file('boothook.sh'), 'boothook.sh',
                    'cloud-boothook')]
    attachments.append((read_cloudinit_file('part_handler.py'),
                       'part-handler.py'))

    if is_cfntools:
        attachments.append((userdata, 'cfn-userdata', 'x-cfninitdata'))
    elif is_software_config:
        # attempt to parse userdata as a multipart message, and if it
        # is, add each part as an attachment
        userdata_parts = None
        try:
            userdata_parts = email.message_from_string(userdata)
        except:
            pass
        if userdata_parts and userdata_parts.is_multipart():
            for part in userdata_parts.get_payload():
                attachments.append((part.get_payload(),
                                    part.get_filename(),
                                    part.get_content_subtype()))
        else:
            attachments.append((userdata, 'userdata', 'x-shellscript'))

    if is_cfntools:
        attachments.append((read_cloudinit_file('loguserdata.py'),
                           'loguserdata.py', 'x-shellscript'))

    metadata = resource.metadata
    if metadata:
        attachments.append((json.dumps(metadata),
                            'cfn-init-data', 'x-cfninitdata'))

    attachments.append((cfg.CONF.heat_watch_server_url,
                        'cfn-watch-server', 'x-cfninitdata'))

    if is_cfntools:
        attachments.append((cfg.CONF.heat_metadata_server_url,
                            'cfn-metadata-server', 'x-cfninitdata'))

        # Create a boto config which the cfntools on the host use to know
        # where the cfn and cw API's are to be accessed
        cfn_url = urlutils.urlparse(cfg.CONF.heat_metadata_server_url)
        cw_url = urlutils.urlparse(cfg.CONF.heat_watch_server_url)
        is_secure = cfg.CONF.instance_connection_is_secure
        vcerts = cfg.CONF.instance_connection_https_validate_certificates
        boto_cfg = "\n".join(["[Boto]",
                              "debug = 0",
                              "is_secure = %s" % is_secure,
                              "https_validate_certificates = %s" % vcerts,
                              "cfn_region_name = heat",
                              "cfn_region_endpoint = %s" %
                              cfn_url.hostname,
                              "cloudwatch_region_name = heat",
                              "cloudwatch_region_endpoint = %s" %
                              cw_url.hostname])
        attachments.append((boto_cfg,
                            'cfn-boto-cfg', 'x-cfninitdata'))

    subparts = [make_subpart(*args) for args in attachments]
    mime_blob = MIMEMultipart(_subparts=subparts)

    return mime_blob.as_string()
Beispiel #23
0
 def __init__(self, url):
     self.url = urlutils.urlparse(url)