}
update_allowed_keys = ('Properties',)
update_allowed_properties = ('Size',)
+ attributes_schema = {
+ "InstanceList": ("A comma-delimited list of server ip addresses. "
+ "(Heat extension)")
+ }
def handle_create(self):
return self.resize(int(self.properties['Size']), raise_on_error=True)
def FnGetRefId(self):
return unicode(self.name)
- def FnGetAtt(self, key):
+ def _resolve_attribute(self, name):
'''
heat extension: "InstanceList" returns comma delimited list of server
ip addresses.
'''
- if key == 'InstanceList':
+ if name == 'InstanceList':
if self.resource_id is None:
- return ''
+ return None
name_list = sorted(self.resource_id.split(','))
inst_list = []
for name in name_list:
# License for the specific language governing permissions and limitations
# under the License.
-from heat.common import exception
-from heat.engine import stack_resource
from heat.common import template_format
+from heat.engine import stack_resource
from heat.openstack.common import log as logging
logger = logging.getLogger(__name__)
'Implemented': False},
}
+ # We only support a couple of the attributes right now
+ attributes_schema = {
+ "Endpoint.Address": "Connection endpoint for the database.",
+ "Endpoint.Port": ("The port number on which the database accepts "
+ "connections.")
+ }
+
def _params(self):
params = {
'KeyName': {'Ref': 'KeyName'},
def handle_delete(self):
self.delete_nested()
- def FnGetAtt(self, key):
+ def _resolve_attribute(self, name):
'''
We don't really support any of these yet.
'''
- if key == 'Endpoint.Address':
+ if name == 'Endpoint.Address':
if self.nested() and 'DatabaseInstance' in self.nested().resources:
return self.nested().resources['DatabaseInstance']._ipaddress()
else:
return '0.0.0.0'
- elif key == 'Endpoint.Port':
+ elif name == 'Endpoint.Port':
return self.properties['Port']
- else:
- raise exception.InvalidTemplateAttribute(resource=self.name,
- key=key)
def resource_mapping():
# under the License.
from heat.engine import clients
-from heat.common import exception
from heat.engine import resource
from heat.openstack.common import log as logging
properties_schema = {'Domain': {'Type': 'String',
'Implemented': False},
'InstanceId': {'Type': 'String'}}
+ attributes_schema = {
+ "AllocationId": ("ID that AWS assigns to represent the allocation of"
+ "the address for use with Amazon VPC. Returned only"
+ " for VPC elastic IP addresses.")
+ }
def __init__(self, name, json_snippet, stack):
super(ElasticIp, self).__init__(name, json_snippet, stack)
def FnGetRefId(self):
return unicode(self._ipaddress())
- def FnGetAtt(self, key):
- if key == 'AllocationId':
+ def _resolve_attribute(self, name):
+ if name == 'AllocationId':
return unicode(self.resource_id)
- else:
- raise exception.InvalidTemplateAttribute(resource=self.name,
- key=key)
class ElasticIpAssociation(resource.Resource):
'UserData': {'Type': 'String'},
'Volumes': {'Type': 'List'}}
+ attributes_schema = {'AvailabilityZone': ('The Availability Zone where the'
+ ' specified instance is '
+ 'launched.'),
+ 'PrivateDnsName': ('Private DNS name of the specified'
+ ' instance.'),
+ 'PublicDnsName': ('Public DNS name of the specified '
+ 'instance.'),
+ 'PrivateIp': ('Private IP address of the specified '
+ 'instance.'),
+ 'PublicIp': ('Public IP address of the specified '
+ 'instance.')}
+
# template keys supported for handle_update, note trailing comma
# is required for a single item to get a tuple not a string
update_allowed_keys = ('Metadata',)
return self.ipaddress or '0.0.0.0'
- def FnGetAtt(self, key):
+ def _resolve_attribute(self, name):
res = None
- if key == 'AvailabilityZone':
+ if name == 'AvailabilityZone':
res = self.properties['AvailabilityZone']
- elif key == 'PublicIp':
- res = self._ipaddress()
- elif key == 'PrivateIp':
- res = self._ipaddress()
- elif key == 'PublicDnsName':
+ elif name in ['PublicIp', 'PrivateIp', 'PublicDnsName',
+ 'PrivateDnsName']:
res = self._ipaddress()
- elif key == 'PrivateDnsName':
- res = self._ipaddress()
- else:
- raise exception.InvalidTemplateAttribute(resource=self.name,
- key=key)
- logger.info('%s.GetAtt(%s) == %s' % (self.name, key, res))
- return unicode(res)
+ logger.info('%s._resolve_attribute(%s) == %s' % (self.name, name, res))
+ return unicode(res) if res else None
def _build_userdata(self, userdata):
if not self.mime_string:
# License for the specific language governing permissions and limitations
# under the License.
-from heat.engine import clients
-from heat.common import exception
from heat.common import template_format
+from heat.engine import clients
from heat.engine import stack_resource
from heat.openstack.common import log as logging
'Subnets': {'Type': 'List',
'Implemented': False}
}
+ attributes_schema = {
+ "CanonicalHostedZoneName": ("The name of the hosted zone that is "
+ "associated with the LoadBalancer."),
+ "CanonicalHostedZoneNameID": ("The ID of the hosted zone name that is "
+ "associated with the LoadBalancer."),
+ "DNSName": "The DNS name for the LoadBalancer.",
+ "SourceSecurityGroup.GroupName": ("The security group that you can use"
+ " as part of your inbound rules for "
+ "your LoadBalancer's back-end "
+ "instances."),
+ "SourceSecurityGroup.OwnerAlias": "Owner of the source security group."
+ }
update_allowed_keys = ('Properties',)
update_allowed_properties = ('Instances',)
def FnGetRefId(self):
return unicode(self.name)
- def FnGetAtt(self, key):
+ def _resolve_attribute(self, name):
'''
We don't really support any of these yet.
'''
- allow = ('CanonicalHostedZoneName',
- 'CanonicalHostedZoneNameID',
- 'DNSName',
- 'SourceSecurityGroupName',
- 'SourceSecurityGroupOwnerAlias')
-
- if key not in allow:
- raise exception.InvalidTemplateAttribute(resource=self.name,
- key=key)
-
- if key == 'DNSName':
+ if name == 'DNSName':
return self.get_output('PublicIp')
- else:
+ elif name in self.attributes_schema:
+ # Not sure if we should return anything for the other attribs
+ # since they aren't really supported in any meaningful way
return ''
'Default': {}},
'admin_state_up': {'Default': True,
'Type': 'Boolean'}}
+ attributes_schema = {
+ "id": "the unique identifier for this network",
+ "status": "the status of the network",
+ "name": "the name of the network",
+ "subnets": "subnets of this network",
+ "admin_state_up": "the administrative status of the network",
+ "tenant_id": "the tenant owning this network"
+ }
def handle_create(self):
props = self.prepare_properties(
if ex.status_code != 404:
raise ex
- def FnGetAtt(self, key):
- try:
- attributes = self._show_resource()
- except QuantumClientException as ex:
- logger.warn("failed to fetch resource attributes: %s" % str(ex))
- return None
- return self.handle_get_attributes(self.name, key, attributes)
-
def resource_mapping():
if clients.quantumclient is None:
'mac_address': {'Type': 'String'},
'device_id': {'Type': 'String'},
'security_groups': {'Type': 'List'}}
+ attributes_schema = {
+ "admin_state_up": "the administrative state of this port",
+ "device_id": "unique identifier for the device",
+ "device_owner": "name of the network owning the port",
+ "fixed_ips": "fixed ip addresses",
+ "id": "the unique identifier for the port",
+ "mac_address": "mac address of the port",
+ "name": "friendly name of the port",
+ "network_id": "unique identifier for the network owning the port",
+ "security_groups": "a list of security groups for the port",
+ "status": "the status of the port",
+ "tenant_id": "tenant owning the port"
+ }
def handle_create(self):
props = self.prepare_properties(
if ex.status_code != 404:
raise ex
- def FnGetAtt(self, key):
- try:
- attributes = self._show_resource()
- except QuantumClientException as ex:
- logger.warn("failed to fetch resource attributes: %s" % str(ex))
- return None
- return self.handle_get_attributes(self.name, key, attributes)
-
def resource_mapping():
if clients.quantumclient is None:
# License for the specific language governing permissions and limitations
# under the License.
+from quantumclient.common.exceptions import QuantumClientException
+
from heat.common import exception
from heat.engine import resource
('quantum reported unexpected',
attributes['name'], attributes['status']))
+ def _resolve_attribute(self, name):
+ try:
+ attributes = self._show_resource()
+ except QuantumClientException as ex:
+ logger.warn("failed to fetch resource attributes: %s" % str(ex))
+ return None
+ return self.handle_get_attributes(self.name, name, attributes)
+
def FnGetRefId(self):
return unicode(self.resource_id)
'Default': {}},
'admin_state_up': {'Type': 'Boolean',
'Default': True}}
+ attributes_schema = {
+ "status": "the status of the router",
+ "external_gateway_info": "gateway network for the router",
+ "name": "friendly name of the router",
+ "admin_state_up": "administrative state of the router",
+ "tenant_id": "tenant owning the router",
+ "id": "unique identifier for the router"
+ }
def handle_create(self):
props = self.prepare_properties(
if ex.status_code != 404:
raise ex
- def FnGetAtt(self, key):
- try:
- attributes = self._show_resource()
- except QuantumClientException as ex:
- logger.warn("failed to fetch resource attributes: %s" % str(ex))
- return None
- return self.handle_get_attributes(self.name, key, attributes)
-
class RouterInterface(quantum.QuantumResource):
properties_schema = {'router_id': {'Type': 'String',
'Type': 'Map',
'Schema': allocation_schema
}}}
+ attributes_schema = {
+ "name": "friendly name of the subnet",
+ "network_id": "parent network of the subnet",
+ "tenant_id": "tenant owning the subnet",
+ "allocation_pools": "ip allocation pools and their ranges",
+ "gateway_ip": "ip of the subnet's gateway",
+ "ip_version": "ip version for the subnet",
+ "cidr": "CIDR block notation for this subnet",
+ "id": "unique identifier for this subnet",
+ # dns_nameservers isn't in the api docs; is it right?
+ "dns_nameservers": "list of dns nameservers",
+ "enable_dhcp": ("'true' if DHCP is enabled for this subnet; 'false'"
+ "otherwise")
+ }
def handle_create(self):
props = self.prepare_properties(
if ex.status_code != 404:
raise ex
- def FnGetAtt(self, key):
- try:
- attributes = self.quantum().show_subnet(
- self.resource_id)['subnet']
- except QuantumClientException as ex:
- logger.warn("failed to fetch resource attributes: %s" % str(ex))
- return None
- return self.handle_get_attributes(self.name, key, attributes)
+ def _show_resource(self):
+ return self.quantum().show_subnet(self.resource_id)['subnet']
def resource_mapping():
from urlparse import urlparse
-from heat.common import exception
+from heat.engine import clients
from heat.engine import resource
from heat.openstack.common import log as logging
-from heat.engine import clients
logger = logging.getLogger(__name__)
'BucketOwnerFullControl']},
'WebsiteConfiguration': {'Type': 'Map',
'Schema': website_schema}}
+ attributes_schema = {
+ "DomainName": "The DNS name of the specified bucket.",
+ "WebsiteURL": "The website endpoint for the specified bucket."
+ }
def validate(self):
'''
def FnGetRefId(self):
return unicode(self.resource_id)
- def FnGetAtt(self, key):
- url, token_id = self.swift().get_auth()
+ def _resolve_attribute(self, name):
+ url = self.swift().get_auth()[0]
parsed = list(urlparse(url))
- if key == 'DomainName':
+ if name == 'DomainName':
return parsed[1].split(':')[0]
- elif key == 'WebsiteURL':
+ elif name == 'WebsiteURL':
return '%s://%s%s/%s' % (parsed[0], parsed[1], parsed[2],
self.resource_id)
- else:
- raise exception.InvalidTemplateAttribute(resource=self.name,
- key=key)
def resource_mapping():
# License for the specific language governing permissions and limitations
# under the License.
-from heat.common import exception
-from heat.engine import stack_resource
from heat.common import template_format
from heat.common import urlfetch
+from heat.engine import stack_resource
from heat.openstack.common import log as logging
def FnGetRefId(self):
return self.nested().identifier().arn()
- def FnGetAtt(self, key):
- if not key.startswith('Outputs.'):
- raise exception.InvalidTemplateAttribute(
- resource=self.name, key=key)
-
- prefix, dot, op = key.partition('.')
- return unicode(self.get_output(op))
-
def resource_mapping():
return {
# under the License.
from heat.common import exception
+from heat.engine import attributes
from heat.engine import environment
-from heat.engine import resource
from heat.engine import parser
+from heat.engine import resource
from heat.engine import scheduler
from heat.openstack.common import log as logging
def __init__(self, name, json_snippet, stack):
super(StackResource, self).__init__(name, json_snippet, stack)
+ self._outputs_to_attribs(json_snippet)
self._nested = None
+ def _outputs_to_attribs(self, json_snippet):
+ if not self.attributes and 'Outputs' in json_snippet:
+ self.attributes_schema = (
+ attributes.Attributes
+ .schema_from_outputs(json_snippet.get('Outputs')))
+ self.attributes = attributes.Attributes(self.name,
+ self.attributes_schema,
+ self._resolve_attribute)
+
def nested(self):
'''
Return a Stack object representing the nested (child) stack.
Handle the creation of the nested stack from a given JSON template.
'''
template = parser.Template(child_template)
+ self._outputs_to_attribs(child_template)
# Note we disable rollback for nested stacks, since they
# should be rolled back by the parent stack on failure
resource=self.name, key=op)
return stack.output(op)
+
+ def _resolve_attribute(self, name):
+ if name.startswith('Outputs.'):
+ name = name.partition('.')[-1]
+ return unicode(self.get_output(name))
# under the License.
+from heat.common import exception
from heat.common import template_format
from heat.engine.resources import eip
from heat.engine import resource
self.assertRaises(resource.UpdateReplace,
rsrc.handle_update, {}, {}, {})
- self.assertRaises(eip.exception.InvalidTemplateAttribute,
+ self.assertRaises(exception.InvalidTemplateAttribute,
rsrc.FnGetAtt, 'Foo')
finally:
rsrc.handle_update(rsrc.json_snippet, {}, {'Instances': id_list})
self.assertEqual('4.5.6.7', rsrc.FnGetAtt('DNSName'))
- self.assertEqual('', rsrc.FnGetAtt('SourceSecurityGroupName'))
+ self.assertEqual('', rsrc.FnGetAtt('SourceSecurityGroup.GroupName'))
try:
rsrc.FnGetAtt('Foo')
self.assertTrue('AResource' in self.stack)
rsrc = self.stack['AResource']
rsrc.resource_id_set('aaaa')
- self.assertEqual('AResource', rsrc.FnGetAtt('foo'))
+ self.assertEqual('AResource', rsrc.FnGetAtt('Foo'))
for action, status in (
(rsrc.CREATE, rsrc.IN_PROGRESS),
from testtools import skipIf
+from heat.common import exception
from heat.common import template_format
from heat.openstack.common.importutils import try_import
from heat.engine.resources import s3
try:
rsrc.FnGetAtt('Foo')
raise Exception('Expected InvalidTemplateAttribute')
- except s3.exception.InvalidTemplateAttribute:
+ except exception.InvalidTemplateAttribute:
pass
self.assertRaises(resource.UpdateReplace,