@Override
  @DB
  @SuppressWarnings("rawtypes")
  public List<IngressRuleVO> authorizeSecurityGroupIngress(AuthorizeSecurityGroupIngressCmd cmd) {
    Long securityGroupId = cmd.getSecurityGroupId();
    String protocol = cmd.getProtocol();
    Integer startPort = cmd.getStartPort();
    Integer endPort = cmd.getEndPort();
    Integer icmpType = cmd.getIcmpType();
    Integer icmpCode = cmd.getIcmpCode();
    List<String> cidrList = cmd.getCidrList();
    Map groupList = cmd.getUserSecurityGroupList();
    Integer startPortOrType = null;
    Integer endPortOrCode = null;

    // Validate parameters
    SecurityGroup securityGroup = _securityGroupDao.findById(securityGroupId);
    if (securityGroup == null) {
      throw new InvalidParameterValueException(
          "Unable to find security group by id " + securityGroupId);
    }

    if (cidrList == null && groupList == null) {
      throw new InvalidParameterValueException(
          "At least one cidr or at least one security group needs to be specified");
    }

    Account caller = UserContext.current().getCaller();
    Account owner = _accountMgr.getAccount(securityGroup.getAccountId());

    if (owner == null) {
      throw new InvalidParameterValueException(
          "Unable to find security group owner by id=" + securityGroup.getAccountId());
    }

    // Verify permissions
    _accountMgr.checkAccess(caller, null, securityGroup);
    Long domainId = owner.getDomainId();

    if (protocol == null) {
      protocol = NetUtils.ALL_PROTO;
    }

    if (!NetUtils.isValidSecurityGroupProto(protocol)) {
      throw new InvalidParameterValueException("Invalid protocol " + protocol);
    }
    if ("icmp".equalsIgnoreCase(protocol)) {
      if ((icmpType == null) || (icmpCode == null)) {
        throw new InvalidParameterValueException(
            "Invalid ICMP type/code specified, icmpType = "
                + icmpType
                + ", icmpCode = "
                + icmpCode);
      }
      if (icmpType == -1 && icmpCode != -1) {
        throw new InvalidParameterValueException("Invalid icmp type range");
      }
      if (icmpCode > 255) {
        throw new InvalidParameterValueException("Invalid icmp code ");
      }
      startPortOrType = icmpType;
      endPortOrCode = icmpCode;
    } else if (protocol.equals(NetUtils.ALL_PROTO)) {
      if ((startPort != null) || (endPort != null)) {
        throw new InvalidParameterValueException(
            "Cannot specify startPort or endPort without specifying protocol");
      }
      startPortOrType = 0;
      endPortOrCode = 0;
    } else {
      if ((startPort == null) || (endPort == null)) {
        throw new InvalidParameterValueException(
            "Invalid port range specified, startPort = " + startPort + ", endPort = " + endPort);
      }
      if (startPort == 0 && endPort == 0) {
        endPort = 65535;
      }
      if (startPort > endPort) {
        throw new InvalidParameterValueException("Invalid port range " + startPort + ":" + endPort);
      }
      if (startPort > 65535 || endPort > 65535 || startPort < -1 || endPort < -1) {
        throw new InvalidParameterValueException(
            "Invalid port numbers " + startPort + ":" + endPort);
      }

      if (startPort < 0 || endPort < 0) {
        throw new InvalidParameterValueException("Invalid port range " + startPort + ":" + endPort);
      }
      startPortOrType = startPort;
      endPortOrCode = endPort;
    }

    protocol = protocol.toLowerCase();

    List<SecurityGroupVO> authorizedGroups = new ArrayList<SecurityGroupVO>();
    if (groupList != null) {
      Collection userGroupCollection = groupList.values();
      Iterator iter = userGroupCollection.iterator();
      while (iter.hasNext()) {
        HashMap userGroup = (HashMap) iter.next();
        String group = (String) userGroup.get("group");
        String authorizedAccountName = (String) userGroup.get("account");

        if ((group == null) || (authorizedAccountName == null)) {
          throw new InvalidParameterValueException(
              "Invalid user group specified, fields 'group' and 'account' cannot be null, please specify groups in the form:  userGroupList[0].group=XXX&userGroupList[0].account=YYY");
        }

        Account authorizedAccount = _accountDao.findActiveAccount(authorizedAccountName, domainId);
        if (authorizedAccount == null) {
          throw new InvalidParameterValueException(
              "Nonexistent account: "
                  + authorizedAccountName
                  + " when trying to authorize ingress for "
                  + securityGroupId
                  + ":"
                  + protocol
                  + ":"
                  + startPortOrType
                  + ":"
                  + endPortOrCode);
        }

        SecurityGroupVO groupVO =
            _securityGroupDao.findByAccountAndName(authorizedAccount.getId(), group);
        if (groupVO == null) {
          throw new InvalidParameterValueException(
              "Nonexistent group "
                  + group
                  + " for account "
                  + authorizedAccountName
                  + "/"
                  + domainId
                  + " is given, unable to authorize ingress.");
        }

        // Check permissions
        if (domainId != groupVO.getDomainId()) {
          throw new PermissionDeniedException(
              "Can't add security group id="
                  + groupVO.getDomainId()
                  + " as it belongs to different domain");
        }

        authorizedGroups.add(groupVO);
      }
    }

    final Transaction txn = Transaction.currentTxn();
    final Set<SecurityGroupVO> authorizedGroups2 =
        new TreeSet<SecurityGroupVO>(new SecurityGroupVOComparator());

    authorizedGroups2.addAll(authorizedGroups); // Ensure we don't re-lock the same row
    txn.start();

    // Prevents other threads/management servers from creating duplicate ingress rules
    securityGroup = _securityGroupDao.acquireInLockTable(securityGroupId);
    if (securityGroup == null) {
      s_logger.warn("Could not acquire lock on network security group: id= " + securityGroupId);
      return null;
    }
    List<IngressRuleVO> newRules = new ArrayList<IngressRuleVO>();
    try {
      for (final SecurityGroupVO ngVO : authorizedGroups2) {
        final Long ngId = ngVO.getId();
        // Don't delete the referenced group from under us
        if (ngVO.getId() != securityGroup.getId()) {
          final SecurityGroupVO tmpGrp = _securityGroupDao.lockRow(ngId, false);
          if (tmpGrp == null) {
            s_logger.warn("Failed to acquire lock on security group: " + ngId);
            txn.rollback();
            return null;
          }
        }
        IngressRuleVO ingressRule =
            _ingressRuleDao.findByProtoPortsAndAllowedGroupId(
                securityGroup.getId(), protocol, startPortOrType, endPortOrCode, ngVO.getId());
        if (ingressRule != null) {
          continue; // rule already exists.
        }
        ingressRule =
            new IngressRuleVO(
                securityGroup.getId(), startPortOrType, endPortOrCode, protocol, ngVO.getId());
        ingressRule = _ingressRuleDao.persist(ingressRule);
        newRules.add(ingressRule);
      }
      if (cidrList != null) {
        for (String cidr : cidrList) {
          IngressRuleVO ingressRule =
              _ingressRuleDao.findByProtoPortsAndCidr(
                  securityGroup.getId(), protocol, startPortOrType, endPortOrCode, cidr);
          if (ingressRule != null) {
            continue;
          }
          ingressRule =
              new IngressRuleVO(
                  securityGroup.getId(), startPortOrType, endPortOrCode, protocol, cidr);
          ingressRule = _ingressRuleDao.persist(ingressRule);
          newRules.add(ingressRule);
        }
      }
      if (s_logger.isDebugEnabled()) {
        s_logger.debug(
            "Added " + newRules.size() + " rules to security group " + securityGroup.getName());
      }
      txn.commit();
      final ArrayList<Long> affectedVms = new ArrayList<Long>();
      affectedVms.addAll(_securityGroupVMMapDao.listVmIdsBySecurityGroup(securityGroup.getId()));
      scheduleRulesetUpdateToHosts(affectedVms, true, null);
      return newRules;
    } catch (Exception e) {
      s_logger.warn("Exception caught when adding ingress rules ", e);
      throw new CloudRuntimeException("Exception caught when adding ingress rules", e);
    } finally {
      if (securityGroup != null) {
        _securityGroupDao.releaseFromLockTable(securityGroup.getId());
      }
    }
  }