Skip to content

Commit

Permalink
Support security group (#101)
Browse files Browse the repository at this point in the history
* Revert "Revert ":sparkles: IF-6816 add security groups api (#89)" (#96)"

This reverts commit a77f2df.

* Revert "Revert ":sparkles: IF-7067 add security group rules api (#90)" (#95)"

This reverts commit 1edbfbc.

* Revert "Revert ":bug: IF-7068 fix existing api (#91)" (#94)"

This reverts commit c5b99e1.

* ✨ IF-7192 security groups api query support (#93)

- List Security Group API
- List Security Group Rule API

Co-authored-by: a-oi-xon <atsushi_oizaki@xon.jp>
Co-authored-by: a-oi-xon <91597807+a-oi-xon@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 17, 2022
1 parent fc7f45c commit d98580f
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 19 deletions.
189 changes: 188 additions & 1 deletion ecl/network/v2/_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from ecl.network.v2 import physical_port as _physical_port
from ecl.network.v2 import quota as _quota
from ecl.network.v2 import reserved_address as _reserved_address
from ecl.network.v2 import security_group as _security_group
from ecl.network.v2 import security_group_rule as _security_group_rule
from ecl.network.v2 import firewall as _firewall
from ecl.network.v2 import firewall_interface as _firewall_if
from ecl.network.v2 import firewall_plan as _firewall_plan
Expand Down Expand Up @@ -186,7 +188,7 @@ def get_extension(self, extension):
def create_port(self, admin_state_up=None, allowed_address_pairs=None,
mac_address=None, description=None, device_id=None,
device_owner=None, fixed_ips=None, name=None,
network_id=None, segmentation_id=None,
network_id=None, security_groups=None, segmentation_id=None,
segmentation_type=None, tags=None):
"""Create a new port from attributes
Expand All @@ -201,6 +203,7 @@ def create_port(self, admin_state_up=None, allowed_address_pairs=None,
e.g. [{"ip_address":<ipv4> , "subnet_id": <uuid> }, ]
:param string name: The name of port to create
:param string network_id: The network id of port to create
:param array security_groups: The security groups ids of port to create
:param int segmentation_id: The segmentation id of port to create
:param string segmentation_type: The segmentation type of port to create
:param dict tags: tags of port
Expand All @@ -227,6 +230,8 @@ def create_port(self, admin_state_up=None, allowed_address_pairs=None,
body["name"] = name
if network_id:
body["network_id"] = network_id
if security_groups:
body["security_groups"] = security_groups
if segmentation_id:
body["segmentation_id"] = segmentation_id
if segmentation_type:
Expand Down Expand Up @@ -303,6 +308,7 @@ def update_port(self, port, **params):
* array fixed_ips: The fixed ips of port to update
e.g. [{"ip_address":<ipv4> , "subnet_id": <uuid> }, ]
* string name: The name of port to update
* array security_groups: The security groups ids of port to update
* int segmentation_id: The segmentation id of port to update
* string segmentation_type: The segmentation type of port to update
* dict tags: tags of port
Expand Down Expand Up @@ -496,6 +502,187 @@ def get_reserved_address(self, reserved_address):
"""
return self._get(_reserved_address.ReservedAddress, reserved_address)

def security_groups(self, **query):
""" List all visible security-groups.
:param query: Query parameters to select results
:return: A list of security-group objects
:rtype: :class:`~ecl.network.v2.security_group.SecurityGroup`
"""
return list(self._list(_security_group.SecurityGroup,
paginated=False, **query))

def create_security_group(self, description=None, name=None, tags=None,
tenant_id=None):
"""Create security-group.
:param string description: Security group description.
:param string name: Security group name.
:param dict tags: Security Group tags.
:param string tenant_id: The owner name of security group.
:returns: The results of security-group creation
:rtype: :class:`~ecl.network.v2.security_group.SecurityGroup`
"""
body = dict()
if description:
body["description"] = description
if name:
body["name"] = name
if tags:
body["tags"] = tags
if tenant_id:
body["tenant_id"] = tenant_id

return self._create(_security_group.SecurityGroup, **body)

def get_security_group(self, security_group):
"""Show details for security-group.
:param security_group: The value can be the ID of a security-group or
a :class:`~ecl.network.v2.security_group.SecurityGroup` instance.
:returns: One :class:`~ecl.network.v2.security_group.SecurityGroup`
:raises: :class:`~ecl.exceptions.ResourceNotFound`
when no resource can be found.
"""
return self._get(_security_group.SecurityGroup, security_group)

def update_security_group(self, security_group, **params):
"""Update security-group.
:param security_group: Either the id of a security-group or
a :class:`~ecl.network.v2.security_group.SecurityGroup` instance.
:param kwargs params: Parameters for security-group update.
* string description: Security group description.
* string name: Security group name.
* dict tags: Security Group tags.
:returns: The updated security-group
:rtype: :class:`~ecl.network.v2.security_group.SecurityGroup`
"""
if not isinstance(security_group, _security_group.SecurityGroup):
# security_group is the ID
security_group = self._get_resource(_security_group.SecurityGroup,
security_group)
security_group._body.clean()

return self._update(_security_group.SecurityGroup,
security_group, **params)

def delete_security_group(self, security_group, ignore_missing=False):
"""Delete security-group.
:param security_group: The value can be either the ID of
a security-group or
a :class:`~ecl.network.v2.security_group.SecurityGroup` instance.
:param bool ignore_missing: When set to ``False`` :class:
`~ecl.exceptions.ResourceNotFound` will
be raised when the security-group does
not exist. When set to ``True``,
no exception will be set when attempting
to delete a nonexistent security-group.
:returns: ``None``
"""
self._delete(_security_group.SecurityGroup, security_group,
ignore_missing=ignore_missing)

def security_group_rules(self, **query):
""" List all visible security-group-rules.
:param query: Query parameters to select results
:return: A list of security-group-rule objects
:rtype: :class:`~ecl.network.v2.security_group_rule.SecurityGroupRule`
"""
return list(self._list(_security_group_rule.SecurityGroupRule,
paginated=False, **query))

def create_security_group_rule(self, security_group_id, direction,
description=None, ethertype=None,
port_range_max=None, port_range_min=None,
protocol=None, remote_group_id=None,
remote_ip_prefix=None, tenant_id=None):
"""Create security-group-rule.
:param string security_group_id: Security group id.
:param string direction: Direction in which the security group rule
is applied.
:param string description: Security group rule description.
:param string ethertype: Addresses represented in CIDR must match
the ingress or egress rules.
:param int port_range_max: The maximum port number in the range that
is matched by the security group rule.
:param int port_range_min: The minimum port number in the range that
is matched by the security group rule.
:param string protocol: Protocol name or number in string format.
e.g. "ICMP" or "1"
:param string remote_group_id: The remote group UUID to associate
with this security group rule. Only
either one of remote_group_id and
remote_ip_prefix have to be specified.
:param string remote_ip_prefix: The IP address prefix to associate
with this security group rule. Only
either one of remote_group_id and
remote_ip_prefix have to be specified.
:param string tenant_id: The owner name of security group rule.
:returns: The results of security-group-rule creation
:rtype: :class:`~ecl.network.v2.security_group_rule.SecurityGroupRule`
"""
body = {
"security_group_id": security_group_id,
"direction": direction
}
if description:
body["description"] = description
if ethertype:
body["ethertype"] = ethertype
if port_range_max is not None:
body["port_range_max"] = port_range_max
if port_range_min is not None:
body["port_range_min"] = port_range_min
if protocol:
body["protocol"] = protocol
if remote_group_id:
body["remote_group_id"] = remote_group_id
if remote_ip_prefix:
body["remote_ip_prefix"] = remote_ip_prefix
if tenant_id:
body["tenant_id"] = tenant_id

return self._create(_security_group_rule.SecurityGroupRule, **body)

def get_security_group_rule(self, security_group_rule):
"""Show details for security-group-rule.
:param security_group_rule: The value can be the ID of
a security-group-rule or
a :class:`~ecl.network.v2.security_group_rule.SecurityGroupRule`
instance.
:returns: :class:`~ecl.network.v2.security_group_rule.SecurityGroupRule`
:raises: :class:`~ecl.exceptions.ResourceNotFound`
when no resource can be found.
"""
return self._get(_security_group_rule.SecurityGroupRule,
security_group_rule)

def delete_security_group_rule(self, security_group_rule,
ignore_missing=False):
"""Delete security-group-rule.
:param security_group_rule: The value can be either the ID of
a security-group-rule or
a :class:`~ecl.network.v2.security_group_rule.SecurityGroupRule`
instance.
:param bool ignore_missing: When set to ``False`` :class:
`~ecl.exceptions.ResourceNotFound` will
be raised when the security-group-rule does
not exist. When set to ``True``,
no exception will be set when attempting
to delete a nonexistent security-group-rule.
:returns: ``None``
"""
self._delete(_security_group_rule.SecurityGroupRule,
security_group_rule, ignore_missing=ignore_missing)

def firewalls(self, **query):
"""
List all visible firewalls.
Expand Down
3 changes: 3 additions & 0 deletions ecl/network/v2/port.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ class Port(base.NetworkBaseResource):
#: users can specify a project ID other than their own.
project_id = resource2.Body('tenant_id')

#: The IDs of security groups applied to the port.
security_groups = resource2.Body('security_groups')

#: The segmentation ID of ports.
segmentation_id = resource2.Body('segmentation_id', type=int)

Expand Down
2 changes: 2 additions & 0 deletions ecl/network/v2/quota.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Quota(resource2.Resource):
vpn_gateway = resource2.Body('vpn_gateway', type=int)
#: The maximum amount of public ip you can create. *Type: int*
public_ip = resource2.Body('public_ip', type=int)
#: The maximum amount of security group you can create. *Type: int*
security_group = resource2.Body('security_group', type=int)


class QuotaDefault(Quota):
Expand Down
39 changes: 39 additions & 0 deletions ecl/network/v2/security_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-

from ecl.network import network_service
from ecl import resource2


class SecurityGroup(resource2.Resource):
"""SecurityGroup Resource"""
resource_key = 'security_group'
resources_key = 'security_groups'
service = network_service.NetworkService("v2.0")
base_path = '/' + service.version + '/security-groups'

# query parameter names
_query_mapping = resource2.QueryParameters(
'description', 'id', 'name', 'status', 'tenant_id')

# capabilities
allow_list = True
allow_create = True
allow_get = True
allow_update = True
allow_delete = True

# Properties
# Security group description.
description = resource2.Body('description')
# Security group unique id.
id = resource2.Body('id')
# Security group name.
name = resource2.Body('name')
# Security group status.
status = resource2.Body('status')
# Security Group tags.
tags = resource2.Body('tags')
# The owner name of security group.
tenant_id = resource2.Body('tenant_id')
# Security group rules
security_group_rules = resource2.Body('security_group_rules', type=list)
52 changes: 52 additions & 0 deletions ecl/network/v2/security_group_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-

from ecl.network import network_service
from ecl import resource2


class SecurityGroupRule(resource2.Resource):
"""SecurityGroupRule Resource"""
resource_key = 'security_group_rule'
resources_key = 'security_group_rules'
service = network_service.NetworkService("v2.0")
base_path = '/' + service.version + '/security-group-rules'

# query parameter names
_query_mapping = resource2.QueryParameters(
'description', 'direction', 'ethertype', 'id', 'port_range_max',
'port_range_min', 'protocol', 'remote_group_id', 'remote_ip_prefix',
'security_group_id', 'tenant_id')

# capabilities
allow_list = True
allow_create = True
allow_get = True
allow_delete = True

# Properties
# Security group rule description.
description = resource2.Body('description')
# Direction in which the security group rule is applied.
direction = resource2.Body('direction')
# Addresses represented in CIDR must match the ingress or egress rules.
ethertype = resource2.Body('ethertype')
# Security group rule unique id.
id = resource2.Body('id')
# The maximum port number in the range that is matched
# by the security group rule.
port_range_max = resource2.Body('port_range_max', type=int)
# The minimum port number in the range that is matched
# by the security group rule.
port_range_min = resource2.Body('port_range_min', type=int)
# Protocol name or number in string format. e.g. "ICMP" or "1"
protocol = resource2.Body('protocol')
# The remote group UUID to associate with this security group rule. Only
# either one of remote_group_id and remote_ip_prefix have to be specified.
remote_group_id = resource2.Body('remote_group_id')
# The IP address prefix to associate with this security group rule. Only
# either one of remote_group_id and remote_ip_prefix have to be specified.
remote_ip_prefix = resource2.Body('remote_ip_prefix')
# Security group id.
security_group_id = resource2.Body('security_group_id')
# The owner name of security group rule.
tenant_id = resource2.Body('tenant_id')
12 changes: 8 additions & 4 deletions ecl/tests/unit/network/v2/test_port.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import testtools

from ecl.network.v2 import port
from ecl.network.v2.port import Port

IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
Expand All @@ -30,6 +30,9 @@
"mac_address": "test-mac",
"name": "Example port 1",
"network_id": IDENTIFIER,
"security_groups": [
IDENTIFIER
],
"segmentation_id": 0,
"segmentation_type": "flat",
"tags": {
Expand All @@ -44,10 +47,10 @@
class TestPort(testtools.TestCase):

def test_basic(self):
sot = port.Port()
sot = Port()
self.assertEqual('port', sot.resource_key)
self.assertEqual('ports', sot.resources_key)
self.assertEqual('/ports', sot.base_path)
self.assertEqual('/v2.0/ports', sot.base_path)
self.assertEqual('network', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertTrue(sot.allow_get)
Expand All @@ -56,7 +59,7 @@ def test_basic(self):
self.assertTrue(sot.allow_list)

def test_make_it(self):
sot = port.Port(**EXAMPLE)
sot = Port(**EXAMPLE)
self.assertTrue(sot.admin_state_up)
self.assertEqual('UP', sot.admin_state)
self.assertEqual(EXAMPLE['allowed_address_pairs'],
Expand All @@ -68,6 +71,7 @@ def test_make_it(self):
self.assertEqual(EXAMPLE['mac_address'], sot.mac_address)
self.assertEqual(EXAMPLE['name'], sot.name)
self.assertEqual(EXAMPLE['network_id'], sot.network_id)
self.assertEqual(EXAMPLE['security_groups'], sot.security_groups)
self.assertEqual(EXAMPLE['segmentation_id'], sot.segmentation_id)
self.assertEqual(EXAMPLE['segmentation_type'], sot.segmentation_type)
self.assertEqual(EXAMPLE['tags'], sot.tags)
Expand Down
Loading

0 comments on commit d98580f

Please sign in to comment.