Skip to content

Commit 9bc1588

Browse files
author
Matous Mojzis
committed
implemented SecurityGroup entity for AWS EC2
1 parent 4b11eeb commit 9bc1588

File tree

3 files changed

+222
-4
lines changed

3 files changed

+222
-4
lines changed

wrapanapi/entities/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
from .server import Server, ServerState
1111
from .network import Network, NetworkMixin
1212
from .volume import Volume, VolumeMixin
13+
from .security_group import SecurityGroup, SecurityGroupMixin
1314

1415
__all__ = [
1516
'Template', 'TemplateMixin', 'Vm', 'VmState', 'VmMixin', 'Instance',
1617
'PhysicalContainer', 'Server', 'ServerState', 'Stack', 'StackMixin',
17-
'Network', 'NetworkMixin', 'Volume', 'VolumeMixin'
18+
'Network', 'NetworkMixin', 'Volume', 'VolumeMixin', 'SecurityGroup',
19+
'SecurityGroupMixin'
1820
]
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""
2+
wrapanapi.entities.security_group
3+
4+
SecurityGroups
5+
"""
6+
7+
from abc import ABCMeta, abstractmethod
8+
9+
from wrapanapi.entities.base import Entity, EntityMixin
10+
from wrapanapi.exceptions import MultipleItemsError, NotFoundError
11+
12+
13+
class SecurityGroup(Entity, metaclass=ABCMeta):
14+
"""
15+
Defines methods/properties pertaining to security groups
16+
"""
17+
@abstractmethod
18+
def get_details(self):
19+
"""
20+
Return a dict with detailed info about this object
21+
22+
There's no specific prescription for how this dict should be formatted--
23+
it will vary based on entity and provider type. It is recommended
24+
that the values contain simple python data types instead of
25+
complex classes so the data can be parsed easily.
26+
27+
Returns: dict
28+
"""
29+
30+
31+
class SecurityGroupMixin(EntityMixin, metaclass=ABCMeta):
32+
"""
33+
Defines methods for systems that support security groups
34+
"""
35+
@abstractmethod
36+
def create_sec_group(self, **kwargs):
37+
"""
38+
Creates security group
39+
40+
Returns: wrapanapi.entities.SecurityGroup for newly created SecurityGroup
41+
"""
42+
43+
@abstractmethod
44+
def list_sec_groups(self, **kwargs):
45+
"""
46+
Return a list of SecurityGroup entities.
47+
48+
Returns: list of SecurityGroup objects
49+
"""
50+
51+
@abstractmethod
52+
def find_sec_groups(self, name, **kwargs):
53+
"""
54+
Find a security group based on 'name' or other kwargs
55+
56+
Returns an empty list if no matches found
57+
58+
Returns: implementation of wrapanapi.network.SecurityGroup
59+
"""
60+
61+
@abstractmethod
62+
def get_sec_group(self, name, **kwargs):
63+
"""
64+
Get a security group based on name or other kwargs
65+
66+
Returns: SecurityGroup object
67+
Raises:
68+
MultipleItemsError if multiple matches found
69+
NotFoundError if unable to find security group
70+
"""
71+
72+
def does_sec_group_exist(self, name):
73+
"""
74+
Checks if a security group with 'name' exists on the system
75+
76+
If multiple security groups with the same name exists, this still returns 'True'
77+
"""
78+
try:
79+
return bool(self.get_sec_group(name))
80+
except MultipleItemsError:
81+
return True
82+
except NotFoundError:
83+
return False

wrapanapi/systems/ec2.py

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
client as boto3client
1313
)
1414

15-
from wrapanapi.entities import (Instance, Network, NetworkMixin, Stack, StackMixin,
16-
Template, TemplateMixin, VmMixin, VmState, Volume)
15+
from wrapanapi.entities import (Instance, Network, NetworkMixin, SecurityGroup, SecurityGroupMixin,
16+
Stack, StackMixin, Template, TemplateMixin, VmMixin, VmState,
17+
Volume)
1718
from wrapanapi.exceptions import (ActionTimedOutError, MultipleItemsError, NotFoundError)
1819
from wrapanapi.systems.base import System
1920

@@ -449,7 +450,98 @@ def cleanup(self):
449450
return self.delete()
450451

451452

452-
class EC2System(System, VmMixin, TemplateMixin, StackMixin, NetworkMixin):
453+
class SecurityGroup(_TagMixin, _SharedMethodsMixin, SecurityGroup):
454+
def __init__(self, system, raw=None, **kwargs):
455+
"""
456+
Constructor for an SecurityGroup tied to a specific system.
457+
458+
Args:
459+
system: an EC2System object
460+
raw: the boto.ec2.volume.Volume object if already obtained, or None
461+
uuid: unique ID of the volume
462+
"""
463+
self._uuid = raw.id if raw else kwargs.get('uuid')
464+
if not self._uuid:
465+
raise ValueError("missing required kwarg: 'uuid'")
466+
467+
super(SecurityGroup, self).__init__(system, raw, **kwargs)
468+
469+
self._api = self.system.ec2_connection
470+
471+
@property
472+
def name(self):
473+
tag_value = self.get_tag_value('Name')
474+
return tag_value if tag_value else self.raw.group_name
475+
476+
def set_rule(self, permission_list, traffic_type='inbound'):
477+
"""
478+
479+
Args:
480+
permission_list= [{
481+
'FromPort': from_port(-1 - all ports),
482+
'ToPort': to_port(-1 - all ports),
483+
'IpProtocol': 'tcp/udp/icmp/icmpv6/-1(all protocols)',
484+
'IpRanges': [{'CidrIp': '0.0.0.0/32'}]
485+
}]
486+
traffic_type: inbound/outbound
487+
488+
Returns:
489+
490+
"""
491+
try:
492+
if traffic_type == 'inbound':
493+
self.raw.authorize_ingress(IpPermissions=permission_list)
494+
else:
495+
self.raw.authorize_egress(IpPermissions=permission_list)
496+
self.refresh()
497+
return True
498+
except Exception:
499+
return False
500+
501+
def unset_rule(self, permission_list, traffic_type='inbound'):
502+
"""
503+
504+
Args:
505+
permission_list: [
506+
FromPort: from_port(-1 - all ports),
507+
ToPort: to_port(-1 - all ports),
508+
IpProtocol: 'tcp/udp/icmp/icmpv6/-1(all protocols)',
509+
IpRanges: [CidrIp: '0.0.0.0/32']
510+
]
511+
type: inbound/outbound
512+
513+
Returns:
514+
515+
"""
516+
try:
517+
if traffic_type == 'inbound':
518+
self.raw.revoke_ingress(IpPermissions=permission_list)
519+
else:
520+
self.raw.revoke_egress(IpPermissions=permission_list)
521+
self.refresh()
522+
return True
523+
except Exception:
524+
return False
525+
526+
def delete(self):
527+
"""
528+
Delete SecurityGroup
529+
"""
530+
self.logger.info("Deleting SecurityGroup '%s', id: '%s'", self.name, self.uuid)
531+
try:
532+
self.raw.delete()
533+
return True
534+
except ActionTimedOutError:
535+
return False
536+
537+
def cleanup(self):
538+
"""
539+
Cleanup SecurityGroup
540+
"""
541+
return self.delete()
542+
543+
544+
class EC2System(System, VmMixin, TemplateMixin, StackMixin, NetworkMixin, SecurityGroupMixin):
453545
"""EC2 Management System, powered by boto
454546
455547
Wraps the EC2 API
@@ -1512,3 +1604,44 @@ def get_snapshot_id_if_import_completed(self, task_id):
15121604
return result.get("SnapshotId")
15131605
else:
15141606
return False
1607+
1608+
def create_sec_group(self, group_name, desc="SG created for automated test", vpc_id=None):
1609+
sg_kwargs = {'Description': desc, 'GroupName': group_name}
1610+
if vpc_id:
1611+
sg_kwargs['VpcId'] = vpc_id
1612+
result = self.ec2_connection.create_security_group(**sg_kwargs)
1613+
return SecurityGroup(system=self, uuid=result['GroupId'],
1614+
raw=self.ec2_resource.SecurityGroup(result['GroupId']))
1615+
1616+
def get_sec_group(self, name=None, id=None):
1617+
return self._get_resource(SecurityGroup, self.find_sec_groups, name=name, id=id)
1618+
1619+
def list_sec_groups(self):
1620+
"""
1621+
Returns a list of SecurityGroup objects
1622+
"""
1623+
sec_group_list = [
1624+
EBSVolume(system=self, uuid=sec_group['GroupId'], raw=self.ec2_resource.SecurityGroup(
1625+
sec_group['GroupId']))
1626+
for sec_group in self.ec2_connection.describe_security_groups().get('SecurityGroups')
1627+
]
1628+
return sec_group_list
1629+
1630+
def find_sec_groups(self, name=None, id=None):
1631+
"""
1632+
Return list of all security groups with given name or id
1633+
Args:
1634+
name: name to search
1635+
id: id to search
1636+
Returns:
1637+
List of SecurityFGroup objects
1638+
"""
1639+
if not name and not id or name and id:
1640+
raise ValueError("Either name or id must be set and not both!")
1641+
if id:
1642+
sec_groups = self.ec2_connection.describe_security_groups(GroupIds=[id])
1643+
else:
1644+
sec_groups = self.ec2_connection.describe_security_groups(GroupNames=[name])
1645+
return [
1646+
SecurityGroup(system=self, raw=self.ec2_resource.SecurityGroup(sec_group['GroupId']))
1647+
for sec_group in sec_groups.get('SecurityGroups')]

0 commit comments

Comments
 (0)