public List authorizeSecurityGroupRule()

in server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java [618:827]


    public List<SecurityGroupRuleVO> authorizeSecurityGroupRule(final Long securityGroupId, String protocol, Integer startPort, Integer endPort, Integer icmpType,
            Integer icmpCode, final List<String> cidrList, Map groupList, final SecurityRuleType ruleType) {
        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 = CallContext.current().getCallingAccount();
        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, true, securityGroup);
        Long domainId = owner.getDomainId();

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

        if (cidrList != null) {
            for (String cidr : cidrList) {
                if (!NetUtils.isValidIp4Cidr(cidr) && !NetUtils.isValidIp6Cidr(cidr)) {
                    throw new InvalidParameterValueException("Invalid cidr " + cidr);
                }
            }
        }

        //Validate Protocol
        protocol = protocol.trim().toLowerCase();
        //Check if protocol is a number
        if(StringUtils.isNumeric(protocol)){
            int protoNumber = Integer.parseInt(protocol);
            // Deal with ICMP(protocol number 1) specially because it need to be paired with icmp type and code
            if (protoNumber == NetUtils.ICMP_PROTO_NUMBER) {
                protocol = NetUtils.ICMP_PROTO;
                if (icmpCode == null) {
                    icmpCode = -1;
                }
                if (icmpType == null) {
                    icmpType = -1;
                }
            } else if(protoNumber < 0 || protoNumber > 255){
                throw new InvalidParameterValueException("Invalid protocol number: " + protoNumber);
            }
        } else {
            //Protocol is not number
            //Check for valid protocol strings
            if (!NetUtils.isValidSecurityGroupProto(protocol)) {
                throw new InvalidParameterValueException("Invalid protocol " + protocol);
            }
        }
        if (protocol.equals(NetUtils.ICMP_PROTO)) {
            NetUtils.validateIcmpTypeAndCode(icmpType, icmpCode);
            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 (protocol.equals(NetUtils.TCP_PROTO) || protocol.equals(NetUtils.UDP_PROTO)) {
            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;
        } else {
            // in 4.6, the start port and end port are ignored in definition of ProtocolAclRule
            // see core/src/com/cloud/agent/resource/virtualnetwork/facade/SetNetworkAclConfigItem.java
            startPortOrType = 0;
            endPortOrCode = 0;
        }

        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 security group rule  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 security group rule.");
                }

                // 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 Set<SecurityGroupVO> authorizedGroups2 = new TreeSet<SecurityGroupVO>(new SecurityGroupVOComparator());

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

        final Integer startPortOrTypeFinal = startPortOrType;
        final Integer endPortOrCodeFinal = endPortOrCode;
        final String protocolFinal = protocol;
        List<SecurityGroupRuleVO> newRules = Transaction.execute(new TransactionCallback<List<SecurityGroupRuleVO>>() {
            @Override
            public List<SecurityGroupRuleVO> doInTransaction(TransactionStatus status) {
                // Prevents other threads/management servers from creating duplicate security rules
                SecurityGroup securityGroup = _securityGroupDao.acquireInLockTable(securityGroupId);
                if (securityGroup == null) {
                    logger.warn("Could not acquire lock on network security group: {}", securityGroup);
                    return null;
                }
                List<SecurityGroupRuleVO> newRules = new ArrayList<SecurityGroupRuleVO>();
                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) {
                                logger.warn("Failed to acquire lock on security group: {}", ngVO);
                                throw new CloudRuntimeException(String.format("Failed to acquire lock on security group: %s", ngVO));
                            }
                        }
                        SecurityGroupRuleVO securityGroupRule = _securityGroupRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocolFinal, startPortOrTypeFinal,
                                endPortOrCodeFinal, ngVO.getId());
                        if ((securityGroupRule != null) && (securityGroupRule.getRuleType() == ruleType)) {
                            logger.warn("The rule {} already exists.", securityGroupRule);
                            continue; // rule already exists.
                        }
                        securityGroupRule = new SecurityGroupRuleVO(ruleType, securityGroup.getId(), startPortOrTypeFinal, endPortOrCodeFinal, protocolFinal, ngVO.getId());
                        securityGroupRule = _securityGroupRuleDao.persist(securityGroupRule);
                        newRules.add(securityGroupRule);
                    }
                    if (cidrList != null) {
                        for (String cidr : cidrList) {
                            SecurityGroupRuleVO securityGroupRule = _securityGroupRuleDao.findByProtoPortsAndCidr(securityGroup.getId(), protocolFinal, startPortOrTypeFinal,
                                    endPortOrCodeFinal, cidr);
                            if ((securityGroupRule != null) && (securityGroupRule.getRuleType() == ruleType)) {
                                continue;
                            }
                            securityGroupRule = new SecurityGroupRuleVO(ruleType, securityGroup.getId(), startPortOrTypeFinal, endPortOrCodeFinal, protocolFinal, cidr);
                            securityGroupRule = _securityGroupRuleDao.persist(securityGroupRule);
                            newRules.add(securityGroupRule);
                        }
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Added {} rules to security group {}", newRules.size(), securityGroup);
                    }
                    return newRules;
                } catch (Exception e) {
                    logger.warn("Exception caught when adding security group rules ", e);
                    throw new CloudRuntimeException("Exception caught when adding security group rules", e);
                } finally {
                    if (securityGroup != null) {
                        _securityGroupDao.releaseFromLockTable(securityGroup.getId());
                    }
                }
            }
        });

        messageBus.publish(_name, MESSAGE_ADD_SECURITY_GROUP_RULE_EVENT, PublishScope.LOCAL, newRules);

        try {
            final ArrayList<Long> affectedVms = new ArrayList<Long>();
            affectedVms.addAll(_securityGroupVMMapDao.listVmIdsBySecurityGroup(securityGroup.getId()));
            scheduleRulesetUpdateToHosts(affectedVms, true, null);
        } catch (Exception e) {
            logger.debug("can't update rules on host, ignore", e);
        }

        return newRules;
    }