text: # Copyright (c) 2006-2011 Mitch Garnaat http://garnaat.org/
  # Copyright (c) 2011, Eucalyptus Systems, Inc.
  #
  # Permission is hereby granted, free of charge, to any person obtaining a
  # copy of this software and associated documentation files (the
  # "Software"), to deal in the Software without restriction, including
  # without limitation the rights to use, copy, modify, merge, publish, dis-
  # tribute, sublicense, and/or sell copies of the Software, and to permit
  # persons to whom the Software is furnished to do so, subject to the fol-
  # lowing conditions:
  #
  # The above copyright notice and this permission notice shall be included
  # in all copies or substantial portions of the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
  # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
  # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  # IN THE SOFTWARE.
  
  """
  Represents an EC2 Security Group
  """
  from boto.ec2.ec2object import EC2Object, TaggedEC2Object
  from boto.exception import BotoClientError
  
  
  class SecurityGroup(TaggedEC2Object):
  
      def __init__(self, connection=None, owner_id=None,
                   name=None, description=None, id=None):
          TaggedEC2Object.__init__(self, connection)
          self.id = id
          self.owner_id = owner_id
          self.name = name
          self.description = description
          self.vpc_id = None
          self.rules = IPPermissionsList()
          self.rules_egress = IPPermissionsList()
  
      def __repr__(self):
          return 'SecurityGroup:%s' % self.name
  
      def startElement(self, name, attrs, connection):
          retval = TaggedEC2Object.startElement(self, name, attrs, connection)
          if retval is not None:
              return retval
          if name == 'ipPermissions':
              return self.rules
          elif name == 'ipPermissionsEgress':
              return self.rules_egress
          else:
              return None
  
      def endElement(self, name, value, connection):
          if name == 'ownerId':
              self.owner_id = value
          elif name == 'groupId':
              self.id = value
          elif name == 'groupName':
              self.name = value
          elif name == 'vpcId':
              self.vpc_id = value
          elif name == 'groupDescription':
              self.description = value
          elif name == 'ipRanges':
              pass
          elif name == 'return':
              if value == 'false':
                  self.status = False
              elif value == 'true':
                  self.status = True
              else:
                  raise Exception(
                      'Unexpected value of status %s for group %s' % (
                          value,
                          self.name
                      )
                  )
          else:
              setattr(self, name, value)
  
      def delete(self):
          if self.vpc_id:
              return self.connection.delete_security_group(group_id=self.id)
          else:
              return self.connection.delete_security_group(self.name)
  
      def add_rule(self, ip_protocol, from_port, to_port,
                   src_group_name, src_group_owner_id, cidr_ip, src_group_group_id):
          """
          Add a rule to the SecurityGroup object.  Note that this method
          only changes the local version of the object.  No information
          is sent to EC2.
          """
          rule = IPPermissions(self)
          rule.ip_protocol = ip_protocol
          rule.from_port = from_port
          rule.to_port = to_port
          self.rules.append(rule)
          rule.add_grant(src_group_name, src_group_owner_id, cidr_ip, src_group_group_id)
  
      def remove_rule(self, ip_protocol, from_port, to_port,
                      src_group_name, src_group_owner_id, cidr_ip, src_group_group_id):
          """
          Remove a rule to the SecurityGroup object.  Note that this method
          only changes the local version of the object.  No information
          is sent to EC2.
          """
          target_rule = None
          for rule in self.rules:
              if rule.ip_protocol == ip_protocol:
                  if rule.from_port == from_port:
                      if rule.to_port == to_port:
                          target_rule = rule
                          target_grant = None
                          for grant in rule.grants:
                              if grant.name == src_group_name or grant.group_id == src_group_group_id:
                                  if grant.owner_id == src_group_owner_id:
                                      if grant.cidr_ip == cidr_ip:
                                          target_grant = grant
                          if target_grant:
                              rule.grants.remove(target_grant)
          if len(rule.grants) == 0:
              self.rules.remove(target_rule)
  
      def authorize(self, ip_protocol=None, from_port=None, to_port=None,
                    cidr_ip=None, src_group=None):
          """
          Add a new rule to this security group.
          You need to pass in either src_group_name
          OR ip_protocol, from_port, to_port,
          and cidr_ip.  In other words, either you are authorizing another
          group or you are authorizing some ip-based rule.
  
          :type ip_protocol: string
          :param ip_protocol: Either tcp | udp | icmp
  
          :type from_port: int
          :param from_port: The beginning port number you are enabling
  
          :type to_port: int
          :param to_port: The ending port number you are enabling
  
          :type cidr_ip: string or list of strings
          :param cidr_ip: The CIDR block you are providing access to.
                          See http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
  
          :type src_group: :class:`boto.ec2.securitygroup.SecurityGroup` or
                           :class:`boto.ec2.securitygroup.GroupOrCIDR`
          :param src_group: The Security Group you are granting access to.
  
          :rtype: bool
          :return: True if successful.
          """
          group_name = None
          if not self.vpc_id:
              group_name = self.name
          group_id = None
          if self.vpc_id:
              group_id = self.id
          src_group_name = None
          src_group_owner_id = None
          src_group_group_id = None
          if src_group:
              cidr_ip = None
              src_group_owner_id = src_group.owner_id
              if not self.vpc_id:
                  src_group_name = src_group.name
              else:
                  if hasattr(src_group, 'group_id'):
                      src_group_group_id = src_group.group_id
                  else:
                      src_group_group_id = src_group.id
          status = self.connection.authorize_security_group(group_name,
                                                            src_group_name,
                                                            src_group_owner_id,
                                                            ip_protocol,
                                                            from_port,
                                                            to_port,
                                                            cidr_ip,
                                                            group_id,
                                                            src_group_group_id)
          if status:
              if not isinstance(cidr_ip, list):
                  cidr_ip = [cidr_ip]
              for single_cidr_ip in cidr_ip:
                  self.add_rule(ip_protocol, from_port, to_port, src_group_name,
                                src_group_owner_id, single_cidr_ip, src_group_group_id)
          return status
  
      def revoke(self, ip_protocol=None, from_port=None, to_port=None,
                 cidr_ip=None, src_group=None):
          group_name = None
          if not self.vpc_id:
              group_name = self.name
          group_id = None
          if self.vpc_id:
              group_id = self.id
          src_group_name = None
          src_group_owner_id = None
          src_group_group_id = None
          if src_group:
              cidr_ip = None
              src_group_owner_id = src_group.owner_id
              if not self.vpc_id:
                  src_group_name = src_group.name
              else:
                  if hasattr(src_group, 'group_id'):
                      src_group_group_id = src_group.group_id
                  else:
                      src_group_group_id = src_group.id
          status = self.connection.revoke_security_group(group_name,
                                                         src_group_name,
                                                         src_group_owner_id,
                                                         ip_protocol,
                                                         from_port,
                                                         to_port,
                                                         cidr_ip,
                                                         group_id,
                                                         src_group_group_id)
          if status:
              self.remove_rule(ip_protocol, from_port, to_port, src_group_name,
                               src_group_owner_id, cidr_ip, src_group_group_id)
          return status
  
      def copy_to_region(self, region, name=None):
          """
          Create a copy of this security group in another region.
          Note that the new security group will be a separate entity
          and will not stay in sync automatically after the copy
          operation.
  
          :type region: :class:`boto.ec2.regioninfo.RegionInfo`
          :param region: The region to which this security group will be copied.
  
          :type name: string
          :param name: The name of the copy.  If not supplied, the copy
                       will have the same name as this security group.
  
          :rtype: :class:`boto.ec2.securitygroup.SecurityGroup`
          :return: The new security group.
          """
          if region.name == self.region:
              raise BotoClientError('Unable to copy to the same Region')
          conn_params = self.connection.get_params()
          rconn = region.connect(**conn_params)
          sg = rconn.create_security_group(name or self.name, self.description)
          source_groups = []
          for rule in self.rules:
              for grant in rule.grants:
                  grant_nom = grant.name or grant.group_id
                  if grant_nom:
                      if grant_nom not in source_groups:
                          source_groups.append(grant_nom)
                          sg.authorize(None, None, None, None, grant)
                  else:
                      sg.authorize(rule.ip_protocol, rule.from_port, rule.to_port,
                                   grant.cidr_ip)
          return sg
  
      def instances(self):
          """
          Find all of the current instances that are running within this
          security group.
  
          :rtype: list of :class:`boto.ec2.instance.Instance`
          :return: A list of Instance objects
          """
          rs = []
          if self.vpc_id:
              rs.extend(self.connection.get_all_reservations(
                  filters={'instance.group-id': self.id}
              ))
          else:
              rs.extend(self.connection.get_all_reservations(
                  filters={'group-id': self.id}
              ))
          instances = [i for r in rs for i in r.instances]
          return instances
  
  
  class IPPermissionsList(list):
  
      def startElement(self, name, attrs, connection):
          if name == 'item':
              self.append(IPPermissions(self))
              return self[-1]
          return None
  
      def endElement(self, name, value, connection):
          pass
  
  
  class IPPermissions(object):
  
      def __init__(self, parent=None):
          self.parent = parent
          self.ip_protocol = None
          self.from_port = None
          self.to_port = None
          self.grants = []
  
      def __repr__(self):
          return 'IPPermissions:%s(%s-%s)' % (self.ip_protocol,
                                              self.from_port, self.to_port)
  
      def startElement(self, name, attrs, connection):
          if name == 'item':
              self.grants.append(GroupOrCIDR(self))
              return self.grants[-1]
          return None
  
      def endElement(self, name, value, connection):
          if name == 'ipProtocol':
              self.ip_protocol = value
          elif name == 'fromPort':
              self.from_port = value
          elif name == 'toPort':
              self.to_port = value
          else:
              setattr(self, name, value)
  
      def add_grant(self, name=None, owner_id=None, cidr_ip=None, group_id=None):
          grant = GroupOrCIDR(self)
          grant.owner_id = owner_id
          grant.group_id = group_id
          grant.name = name
          grant.cidr_ip = cidr_ip
          self.grants.append(grant)
          return grant
  
  
  class GroupOrCIDR(object):
  
      def __init__(self, parent=None):
          self.owner_id = None
          self.group_id = None
          self.name = None
          self.cidr_ip = None
  
      def __repr__(self):
          if self.cidr_ip:
              return '%s' % self.cidr_ip
          else:
              return '%s-%s' % (self.name or self.group_id, self.owner_id)
  
      def startElement(self, name, attrs, connection):
          return None
  
      def endElement(self, name, value, connection):
          if name == 'userId':
              self.owner_id = value
          elif name == 'groupId':
              self.group_id = value
          elif name == 'groupName':
              self.name = value
          if name == 'cidrIp':
              self.cidr_ip = value
          else:
              setattr(self, name, value)
  
  class ExtNetwork(EC2Object):
      def __init__(self, connection = None):
          EC2Object.__init__(self, connection)
          self.extnet_name = None
          self.state = None
          self.availability_zone = None
  
      def endElement(self, name, value, connection):
          if name == "extNetName":
              self.extnet_name = value
          elif name == "state":
              self.state = value
          elif name == 'availabilityZone':
              self.availability_zone = value
          else:
              setattr(self, name, value)
  