Implement DescribeAccountAttributes action

Change-Id: I2e67e88f5e804fc400109c762ca9b9be1a8f930e
This commit is contained in:
Feodor Tersin 2015-02-11 00:50:23 +03:00
parent c754004c21
commit 7377e3f7d9
5 changed files with 157 additions and 11 deletions

View File

@ -16,6 +16,7 @@ from oslo.config import cfg
from ec2api.api import clients
from ec2api.api import common
from ec2api import exception
from ec2api.openstack.common import log as logging
from ec2api import utils
@ -49,13 +50,23 @@ CONF = cfg.CONF
CONF.register_opts(availability_zone_opts)
LOG = logging.getLogger(__name__)
"""Availability zones and regions related API implementation
"""Availability zones, regions, account attributes related API implementation
"""
# TODO(ft): implement messages, regionName AvailabilityZone properties
# TODO(ft): implement vpc-max-security-groups-per-interface, max-elastic-ips,
# vpc-max-elastic-ips Account Attributes
Validator = common.Validator
def get_account_attribute_engine():
if CONF.full_vpc_support:
return AccountAttributeEngineNeutron()
else:
return AccountAttributeEngineNova()
class AvailabilityZoneDescriber(common.UniversalDescriber):
KIND = 'az'
@ -120,6 +131,29 @@ def describe_regions(context, region_name=None, filter=None):
return {'regionInfo': regions}
def describe_account_attributes(context, attribute_name=None):
def get_max_instances():
nova = clients.nova(context)
quotas = nova.quotas.get(context.project_id, context.user_id)
return quotas.instances
attribute_getters = {
'supported-platforms': (
account_attribute_engine.get_supported_platforms),
'default-vpc': account_attribute_engine.get_default_vpc,
'max-instances': get_max_instances,
}
formatted_attributes = []
for attribute in (attribute_name or attribute_getters):
if attribute not in attribute_getters:
raise exception.InvalidParameter(name=attribute)
formatted_attributes.append(
_format_account_attribute(attribute,
attribute_getters[attribute]()))
return {'accountAttributeSet': formatted_attributes}
def _format_availability_zone(zone):
return {'zoneName': zone.zoneName,
'zoneState': ('available'
@ -128,6 +162,13 @@ def _format_availability_zone(zone):
}
def _format_account_attribute(attribute, value):
if not isinstance(value, list):
value = [value]
return {'attributeName': attribute,
'attributeValueSet': [{'attributeValue': val} for val in value]}
# NOTE(Alex): Openstack extension, AWS-incompability
# The whole function and its result is incompatible with AWS.
@ -152,3 +193,24 @@ def _describe_verbose(context):
values['updated_at']))})
return {'availabilityZoneInfo': formatted_availability_zones}
class AccountAttributeEngineNeutron(object):
def get_supported_platforms(self):
return ['EC2', 'VPC']
def get_default_vpc(self):
return 'none'
class AccountAttributeEngineNova(object):
def get_supported_platforms(self):
return ['EC2']
def get_default_vpc(self):
return 'none'
account_attribute_engine = get_account_attribute_engine()

View File

@ -320,7 +320,7 @@ class CloudController(object):
@module_and_param_types(security_group, 'sg_id',
'security_group_str', 'dummy')
def revoke_security_group_ingress(self, context, group_id=None,
group_name=None, ip_permissions=None):
group_name=None, ip_permissions=None):
"""Removes one or more ingress rules from a security group.
Args:
@ -357,7 +357,7 @@ class CloudController(object):
@module_and_param_types(security_group, 'sg_id', 'dummy')
def revoke_security_group_egress(self, context, group_id,
ip_permissions=None):
ip_permissions=None):
"""Removes one or more egress rules from a security group for EC2-VPC.
Args:
@ -676,6 +676,23 @@ class CloudController(object):
Specified regions.
"""
@module_and_param_types(availability_zone, 'strs')
def describe_account_attributes(self, context, attribute_name=None):
"""Describes attributes of your EC2 account.
Args:
context (RequestContext): The request context.
attribute_name (list of str): One or more account attribute names.
The following are the supported account attributes:
supported-platforms | default-vpc | max-instances |
vpc-max-security-groups-per-interface (unsupported now) |
max-elastic-ips (unsupported now) |
vpc-max-elastic-ips (unsupported now)
Returns:
Information about one or more account attributes.
"""
@module_and_param_types(instance, 'i_id')
def get_password_data(self, context, instance_id):
"""Retrieves the encrypted administrator password for Windows instance.
@ -1606,8 +1623,8 @@ class VpcCloudController(CloudController):
Returns:
A list of network interfaces.
"""
return network_interface.describe_network_interfaces(context,
network_interface_id, filter)
return network_interface.describe_network_interfaces(
context, network_interface_id, filter)
@module_and_param_types(network_interface, 'eni_id',
'str')
@ -1635,10 +1652,10 @@ class VpcCloudController(CloudController):
'bool',
'sg_ids')
def modify_network_interface_attribute(self, context,
network_interface_id,
description=None,
source_dest_check=None,
security_group_id=None):
network_interface_id,
description=None,
source_dest_check=None,
security_group_id=None):
"""Modifies the specified attribute of the specified network interface.
@ -1661,8 +1678,8 @@ class VpcCloudController(CloudController):
@module_and_param_types(network_interface, 'eni_id',
'str')
def reset_network_interface_attribute(self, context,
network_interface_id,
attribute):
network_interface_id,
attribute):
"""Resets the specified attribute of the specified network interface.

View File

@ -288,6 +288,10 @@ class MissingParameter(Invalid):
msg_fmt = _("The required parameter '%(param)s' is missing")
class InvalidParameter(Invalid):
msg_fmt = _("The property '%(name)s' is not valid")
class InvalidParameterValue(Invalid):
msg_fmt = _("Value (%(value)s) for parameter %(parameter)s is invalid. "
"%(reason)s")

View File

@ -53,6 +53,7 @@ class ApiTestCase(test_base.BaseTestCase):
self.nova_security_group_rules = (
nova_mock.return_value.security_group_rules)
self.nova_volumes = nova_mock.return_value.volumes
self.nova_quotas = nova_mock.return_value.quotas
self.addCleanup(nova_patcher.stop)
glance_patcher = mock.patch('glanceclient.client.Client')

View File

@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import mock
from ec2api.api import availability_zone
from ec2api.tests.unit import base
from ec2api.tests.unit import fakes
from ec2api.tests.unit import matchers
@ -50,3 +53,62 @@ class AvailabilityZoneCase(base.ApiTestCase):
self.assertEqual(resp['regionInfo'][0]['regionName'], 'nova')
self.assertTrue(resp['regionInfo'][0].get('regionEndpoint')
is not None)
def test_describe_account_attributes(self):
self.nova_quotas.get.return_value = mock.Mock(instances=77)
availability_zone.account_attribute_engine = (
availability_zone.AccountAttributeEngineNeutron())
resp = self.execute('DescribeAccountAttributes', {})
self.assertEqual(200, resp['http_status_code'])
self.assertThat(resp['accountAttributeSet'],
matchers.ListMatches(
[{'attributeName': 'supported-platforms',
'attributeValueSet': [
{'attributeValue': 'EC2'},
{'attributeValue': 'VPC'}]},
{'attributeName': 'default-vpc',
'attributeValueSet': [
{'attributeValue': 'none'}]},
{'attributeName': 'max-instances',
'attributeValueSet': [
{'attributeValue': 77}]}],
orderless_lists=True))
self.nova_quotas.get.assert_called_once_with(
fakes.ID_OS_PROJECT, fakes.ID_OS_USER)
availability_zone.account_attribute_engine = (
availability_zone.AccountAttributeEngineNova())
resp = self.execute('DescribeAccountAttributes', {})
self.assertEqual(200, resp['http_status_code'])
self.assertThat(resp['accountAttributeSet'],
matchers.ListMatches(
[{'attributeName': 'supported-platforms',
'attributeValueSet': [
{'attributeValue': 'EC2'}]},
{'attributeName': 'default-vpc',
'attributeValueSet': [
{'attributeValue': 'none'}]},
{'attributeName': 'max-instances',
'attributeValueSet': [
{'attributeValue': 77}]}],
orderless_lists=True))
resp = self.execute('DescribeAccountAttributes',
{'AttributeName.1': 'default-vpc',
'AttributeName.2': 'max-instances'})
self.assertEqual(200, resp['http_status_code'])
self.assertThat(resp['accountAttributeSet'],
matchers.ListMatches(
[{'attributeName': 'default-vpc',
'attributeValueSet': [
{'attributeValue': 'none'}]},
{'attributeName': 'max-instances',
'attributeValueSet': [
{'attributeValue': 77}]}],
orderless_lists=True))
resp = self.execute('DescribeAccountAttributes',
{'AttributeName.1': 'fake'})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidParameter', resp['Error']['Code'])