mirror of https://github.com/apache/cloudstack.git
resolve conflict
This commit is contained in:
commit
bf8c2ae9bc
5
.github/aw/imports/github/gh-aw/359795d49ada21681ab616bd4cbcb144a7387115/.github_workflows_shared_noop-reminder.md
generated
vendored
Normal file
5
.github/aw/imports/github/gh-aw/359795d49ada21681ab616bd4cbcb144a7387115/.github_workflows_shared_noop-reminder.md
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
**Important**: If no action is needed after completing your analysis, you **MUST** call the `noop` safe-output tool with a brief explanation. Failing to call any safe-output tool is the most common cause of safe-output workflow failures.
|
||||
|
||||
```json
|
||||
{"noop": {"message": "No action needed: [brief explanation of what was analyzed and why]"}}
|
||||
```
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
---
|
||||
# Report formatting guidelines
|
||||
---
|
||||
|
||||
## Report Structure Guidelines
|
||||
|
||||
### 1. Header Levels
|
||||
**Use h3 (###) or lower for all headers in your issue report to maintain proper document hierarchy.**
|
||||
|
||||
When creating GitHub issues or discussions:
|
||||
- Use `###` (h3) for main sections (e.g., "### Test Summary")
|
||||
- Use `####` (h4) for subsections (e.g., "#### Device-Specific Results")
|
||||
- Never use `##` (h2) or `#` (h1) in reports - these are reserved for titles
|
||||
|
||||
### 2. Progressive Disclosure
|
||||
**Wrap detailed test results in `<details><summary><b>Section Name</b></summary>` tags to improve readability and reduce scrolling.**
|
||||
|
||||
Use collapsible sections for:
|
||||
- Verbose details (full test logs, raw data)
|
||||
- Secondary information (minor warnings, extra context)
|
||||
- Per-item breakdowns when there are many items
|
||||
|
||||
Always keep critical information visible (summary, critical issues, key metrics).
|
||||
|
||||
### 3. Report Structure Pattern
|
||||
|
||||
1. **Overview**: 1-2 paragraphs summarizing key findings
|
||||
2. **Critical Information**: Show immediately (summary stats, critical issues)
|
||||
3. **Details**: Use `<details><summary><b>Section Name</b></summary>` for expanded content
|
||||
4. **Context**: Add helpful metadata (workflow run, date, trigger)
|
||||
|
||||
### Design Principles (Airbnb-Inspired)
|
||||
|
||||
Reports should:
|
||||
- **Build trust through clarity**: Most important info immediately visible
|
||||
- **Exceed expectations**: Add helpful context like trends, comparisons
|
||||
- **Create delight**: Use progressive disclosure to reduce overwhelm
|
||||
- **Maintain consistency**: Follow patterns across all reports
|
||||
|
||||
### Example Report Structure
|
||||
|
||||
```markdown
|
||||
### Summary
|
||||
- Key metric 1: value
|
||||
- Key metric 2: value
|
||||
- Status: ✅/⚠️/❌
|
||||
|
||||
### Critical Issues
|
||||
[Always visible - these are important]
|
||||
|
||||
<details>
|
||||
<summary><b>View Detailed Results</b></summary>
|
||||
|
||||
[Comprehensive details, logs, traces]
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>View All Warnings</b></summary>
|
||||
|
||||
[Minor issues and potential problems]
|
||||
|
||||
</details>
|
||||
|
||||
### Recommendations
|
||||
[Actionable next steps - keep visible]
|
||||
```
|
||||
|
||||
## Workflow Run References
|
||||
|
||||
- Format run IDs as links: `[§12345](https://github.com/owner/repo/actions/runs/12345)`
|
||||
- Include up to 3 most relevant run URLs at end under `**References:**`
|
||||
- Do NOT add footer attribution (system adds automatically)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"3cfe810a6f1f402ae902098f1dc14b99a4281a5986d58b76e96eec1a2fde7646","compiler_version":"v0.76.1","strict":true,"agent_id":"copilot"}
|
||||
# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d4b8988df7c60cd416200769cc6bc7f1aab5bb3128df0b9a83a35e061b4da111","compiler_version":"v0.76.1","strict":true,"agent_id":"copilot"}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"46d564922b082d0db93244972e8005ea6904ee5f","version":"v0.76.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55","digest":"sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55","digest":"sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55","digest":"sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.19","digest":"sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]}
|
||||
# ___ _ _
|
||||
# / _ \ | | (_)
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#
|
||||
# Resolved workflow manifest:
|
||||
# Imports:
|
||||
# - github/gh-aw/.github/workflows/shared/noop-reminder.md@359795d49ada21681ab616bd4cbcb144a7387115
|
||||
# - github/gh-aw/.github/workflows/shared/reporting.md@359795d49ada21681ab616bd4cbcb144a7387115
|
||||
#
|
||||
# Secrets used:
|
||||
|
|
@ -197,20 +198,20 @@ jobs:
|
|||
run: |
|
||||
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
|
||||
{
|
||||
cat << 'GH_AW_PROMPT_9a8f42497f411c95_EOF'
|
||||
cat << 'GH_AW_PROMPT_e560c36b9148ef78_EOF'
|
||||
<system>
|
||||
GH_AW_PROMPT_9a8f42497f411c95_EOF
|
||||
GH_AW_PROMPT_e560c36b9148ef78_EOF
|
||||
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
|
||||
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
|
||||
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
|
||||
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
|
||||
cat << 'GH_AW_PROMPT_9a8f42497f411c95_EOF'
|
||||
cat << 'GH_AW_PROMPT_e560c36b9148ef78_EOF'
|
||||
<safe-output-tools>
|
||||
Tools: add_comment, add_labels, missing_tool, missing_data, noop
|
||||
</safe-output-tools>
|
||||
GH_AW_PROMPT_9a8f42497f411c95_EOF
|
||||
GH_AW_PROMPT_e560c36b9148ef78_EOF
|
||||
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
|
||||
cat << 'GH_AW_PROMPT_9a8f42497f411c95_EOF'
|
||||
cat << 'GH_AW_PROMPT_e560c36b9148ef78_EOF'
|
||||
<github-context>
|
||||
The following GitHub context information is available for this workflow:
|
||||
{{#if github.actor}}
|
||||
|
|
@ -239,14 +240,14 @@ jobs:
|
|||
{{/if}}
|
||||
</github-context>
|
||||
|
||||
GH_AW_PROMPT_9a8f42497f411c95_EOF
|
||||
GH_AW_PROMPT_e560c36b9148ef78_EOF
|
||||
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
|
||||
cat << 'GH_AW_PROMPT_9a8f42497f411c95_EOF'
|
||||
cat << 'GH_AW_PROMPT_e560c36b9148ef78_EOF'
|
||||
</system>
|
||||
{{#runtime-import .github/aw/imports/github/gh-aw/359795d49ada21681ab616bd4cbcb144a7387115/.github_workflows_shared_reporting.md}}
|
||||
{{#runtime-import .github/workflows/shared/noop-reminder.md}}
|
||||
{{#runtime-import .github/aw/imports/github/gh-aw/359795d49ada21681ab616bd4cbcb144a7387115/.github_workflows_shared_noop-reminder.md}}
|
||||
{{#runtime-import .github/workflows/issue-triage-agent.md}}
|
||||
GH_AW_PROMPT_9a8f42497f411c95_EOF
|
||||
GH_AW_PROMPT_e560c36b9148ef78_EOF
|
||||
} > "$GH_AW_PROMPT"
|
||||
- name: Interpolate variables and render templates
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
|
|
@ -435,9 +436,9 @@ jobs:
|
|||
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
|
||||
mkdir -p /tmp/gh-aw/safeoutputs
|
||||
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
|
||||
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_40bb017992a44aa9_EOF'
|
||||
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c8eaf0ada4607ff7_EOF'
|
||||
{"add_comment":{"max":1},"add_labels":{"allowed":["bug","feature","enhancement","documentation","question","help-wanted","good-first-issue"]},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG_40bb017992a44aa9_EOF
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG_c8eaf0ada4607ff7_EOF
|
||||
- name: Generate Safe Outputs Tools
|
||||
env:
|
||||
GH_AW_TOOLS_META_JSON: |
|
||||
|
|
@ -653,7 +654,7 @@ jobs:
|
|||
|
||||
mkdir -p /home/runner/.copilot
|
||||
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
|
||||
cat << GH_AW_MCP_CONFIG_8874a73d1c6a94b7_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
|
||||
cat << GH_AW_MCP_CONFIG_88b8311e90d25032_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
|
||||
{
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
|
|
@ -694,7 +695,7 @@ jobs:
|
|||
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
|
||||
}
|
||||
}
|
||||
GH_AW_MCP_CONFIG_8874a73d1c6a94b7_EOF
|
||||
GH_AW_MCP_CONFIG_88b8311e90d25032_EOF
|
||||
- name: Mount MCP servers as CLIs
|
||||
id: mount-mcp-clis
|
||||
continue-on-error: true
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ permissions:
|
|||
issues: read
|
||||
imports:
|
||||
- github/gh-aw/.github/workflows/shared/reporting.md@359795d49ada21681ab616bd4cbcb144a7387115
|
||||
- github/gh-aw/.github/workflows/shared/noop-reminder.md@359795d49ada21681ab616bd4cbcb144a7387115
|
||||
safe-outputs:
|
||||
add-comment: {}
|
||||
add-labels:
|
||||
|
|
@ -88,5 +89,3 @@ This provides both per-issue context and batch visibility.
|
|||
- `question`: Used for issues that are asking for clarification or have questions about the project.
|
||||
- `help-wanted`: Indicates that the issue is a good candidate for external contributions and help
|
||||
- `good-first-issue`: Marks issues that are suitable for newcomers to the project, often with simpler scope.
|
||||
|
||||
{{#runtime-import shared/noop-reminder.md}}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ repos:
|
|||
^server/src/test/resources/certs/rsa_self_signed\.key$|
|
||||
^services/console-proxy/rdpconsole/src/test/doc/rdp-key\.pem$|
|
||||
^systemvm/agent/certs/localhost\.key$|
|
||||
^systemvm/agent/certs/realhostip\.key$|
|
||||
^systemvm/agent/certs/systemvm\.key$|
|
||||
^test/integration/smoke/test_ssl_offloading\.py$
|
||||
- id: end-of-file-fixer
|
||||
exclude: \.vhd$|\.svg$
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ public interface Host extends StateObject<Status>, Identity, Partition, HAResour
|
|||
String HOST_OVFTOOL_VERSION = "host.ovftool.version";
|
||||
String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
|
||||
String HOST_SSH_PORT = "host.ssh.port";
|
||||
String GUEST_OS_CATEGORY_ID = "guest.os.category.id";
|
||||
String GUEST_OS_RULE = "guest.os.rule";
|
||||
|
||||
int DEFAULT_SSH_PORT = 22;
|
||||
|
||||
|
|
|
|||
|
|
@ -449,6 +449,7 @@ public class ApiConstants {
|
|||
public static final String MAX_VGPU_PER_PHYSICAL_GPU = "maxvgpuperphysicalgpu";
|
||||
public static final String GUEST_OS_LIST = "guestoslist";
|
||||
public static final String GUEST_OS_COUNT = "guestoscount";
|
||||
public static final String GUEST_OS_RULE = "guestosrule";
|
||||
public static final String OS_MAPPING_CHECK_ENABLED = "osmappingcheckenabled";
|
||||
public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate";
|
||||
public static final String OUTOFBANDMANAGEMENT_ENABLED = "outofbandmanagementenabled";
|
||||
|
|
@ -602,6 +603,8 @@ public class ApiConstants {
|
|||
public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine";
|
||||
public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot";
|
||||
public static final String TARGET_IQN = "targetiqn";
|
||||
public static final String TARIFF_ID = "tariffid";
|
||||
public static final String TARIFF_NAME = "tariffname";
|
||||
public static final String TASKS_FILTER = "tasksfilter";
|
||||
public static final String TEMPLATE_FILTER = "templatefilter";
|
||||
public static final String TEMPLATE_ID = "templateid";
|
||||
|
|
@ -663,6 +666,7 @@ public class ApiConstants {
|
|||
public static final String VIRTUAL_MACHINE_STATE = "vmstate";
|
||||
public static final String VIRTUAL_MACHINES = "virtualmachines";
|
||||
public static final String USAGE_ID = "usageid";
|
||||
public static final String USAGE_NAME = "usagename";
|
||||
public static final String USAGE_TYPE = "usagetype";
|
||||
public static final String INCLUDE_TAGS = "includetags";
|
||||
|
||||
|
|
@ -879,6 +883,7 @@ public class ApiConstants {
|
|||
public static final String IS_SOURCE_NAT = "issourcenat";
|
||||
public static final String IS_STATIC_NAT = "isstaticnat";
|
||||
public static final String ITERATIONS = "iterations";
|
||||
public static final String ITEMS = "items";
|
||||
public static final String SORT_BY = "sortby";
|
||||
public static final String CHANGE_CIDR = "changecidr";
|
||||
public static final String PURPOSE = "purpose";
|
||||
|
|
|
|||
|
|
@ -49,9 +49,14 @@ public class UpdateHostCmd extends BaseCmd {
|
|||
@Parameter(name = ApiConstants.OS_CATEGORY_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = GuestOSCategoryResponse.class,
|
||||
description = "The ID of OS category to update the host with")
|
||||
description = "the ID of OS category used to prioritize VMs with matching OS category during the allocation process. " +
|
||||
"It cannot be used alongside the 'guestosrule' parameter.")
|
||||
private Long osCategoryId;
|
||||
|
||||
@Parameter(name = ApiConstants.GUEST_OS_RULE, type = CommandType.STRING, description = "the guest OS rule written in JavaScript to match with the OS of the VM." +
|
||||
"It cannot be used alongside the 'oscategoryid' parameter.")
|
||||
private String guestOsRule;
|
||||
|
||||
@Parameter(name = ApiConstants.ALLOCATION_STATE,
|
||||
type = CommandType.STRING,
|
||||
description = "Change resource state of host, valid values are [Enable, Disable]. Operation may failed if host in states not allowing Enable/Disable")
|
||||
|
|
@ -96,6 +101,10 @@ public class UpdateHostCmd extends BaseCmd {
|
|||
return osCategoryId;
|
||||
}
|
||||
|
||||
public String getGuestOsRule() {
|
||||
return guestOsRule;
|
||||
}
|
||||
|
||||
public String getAllocationState() {
|
||||
return allocationState;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,10 @@ public class HostResponse extends BaseResponseWithAnnotations {
|
|||
@Param(description = "The OS category name of the host")
|
||||
private String osCategoryName;
|
||||
|
||||
@SerializedName(ApiConstants.GUEST_OS_RULE)
|
||||
@Param(description = "the guest OS rule")
|
||||
private String guestOsRule;
|
||||
|
||||
@SerializedName(ApiConstants.IP_ADDRESS)
|
||||
@Param(description = "The IP address of the host")
|
||||
private String ipAddress;
|
||||
|
|
@ -999,4 +1003,12 @@ public class HostResponse extends BaseResponseWithAnnotations {
|
|||
public String getExtensionName() {
|
||||
return extensionName;
|
||||
}
|
||||
|
||||
public String getGuestOsRule() {
|
||||
return guestOsRule;
|
||||
}
|
||||
|
||||
public void setGuestOsRule(String guestOsRule) {
|
||||
this.guestOsRule = guestOsRule;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import javax.persistence.Transient;
|
|||
import com.cloud.cpu.CPU;
|
||||
import org.apache.cloudstack.util.CPUArchConverter;
|
||||
import org.apache.cloudstack.util.HypervisorTypeConverter;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
|
|
@ -856,7 +856,8 @@ public class HostVO implements Host {
|
|||
}
|
||||
|
||||
if (BooleanUtils.isTrue(this.getIsTagARule())) {
|
||||
return TagAsRuleHelper.interpretTagAsRule(this.getHostTags().get(0), serviceOffering.getHostTag(), HostTagsDao.hostTagRuleExecutionTimeout.value());
|
||||
return GenericRuleHelper.interpretTagAsRule(this.getHostTags().get(0), serviceOffering.getHostTag(), HostTagsDao.hostTagRuleExecutionTimeout.value(),
|
||||
HostTagsDao.hostTagRuleExecutionTimeout.key());
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(serviceOffering.getHostTag())) {
|
||||
|
|
|
|||
|
|
@ -32,12 +32,17 @@ import com.cloud.utils.Pair;
|
|||
import com.cloud.utils.db.GenericDao;
|
||||
import com.cloud.utils.fsm.StateDao;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
/**
|
||||
* Data Access Object for server
|
||||
*
|
||||
*/
|
||||
public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Status.Event, Host> {
|
||||
|
||||
ConfigKey<Long> guestOsRuleExecutionTimeout = new ConfigKey<>("Advanced", Long.class, "guest.os.rule.execution.timeout", "3000", "The maximum runtime, in milliseconds, " +
|
||||
"to execute a guest OS rule; if it is reached, a timeout will happen.", true);
|
||||
|
||||
long countBy(long clusterId, ResourceState... states);
|
||||
|
||||
Integer countAllByType(final Host.Type type);
|
||||
|
|
@ -220,6 +225,8 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat
|
|||
|
||||
List<HostVO> findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags);
|
||||
|
||||
List<HostVO> findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(String templateGuestOSName);
|
||||
|
||||
List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags);
|
||||
|
||||
List<Long> listSsvmHostsWithPendingMigrateJobsOrderedByJobCount();
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ import javax.persistence.TableGenerator;
|
|||
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import com.cloud.agent.api.VgpuTypesInfo;
|
||||
|
|
@ -84,7 +86,7 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||
|
||||
@DB
|
||||
@TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", valueColumnName = "sequence", allocationSize = 1)
|
||||
public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao { //FIXME: , ExternalIdDao {
|
||||
public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao, Configurable {
|
||||
|
||||
private static final String LIST_HOST_IDS_BY_HOST_TAGS = "SELECT filtered.host_id, COUNT(filtered.tag) AS tag_count "
|
||||
+ "FROM (SELECT host_id, tag, is_tag_a_rule FROM host_tags GROUP BY host_id,tag,is_tag_a_rule) AS filtered "
|
||||
|
|
@ -1520,11 +1522,13 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HostVO> findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags) {
|
||||
List<HostTagVO> hostTagVOList = _hostTagsDao.findHostRuleTags();
|
||||
List<HostVO> result = new ArrayList<>();
|
||||
for (HostTagVO rule: hostTagVOList) {
|
||||
if (TagAsRuleHelper.interpretTagAsRule(rule.getTag(), computeOfferingTags, HostTagsDao.hostTagRuleExecutionTimeout.value())) {
|
||||
if (GenericRuleHelper.interpretTagAsRule(rule.getTag(), computeOfferingTags, HostTagsDao.hostTagRuleExecutionTimeout.value(),
|
||||
HostTagsDao.hostTagRuleExecutionTimeout.key())) {
|
||||
result.add(findById(rule.getHostId()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1532,6 +1536,23 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HostVO> findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(String templateGuestOSName) {
|
||||
List<DetailVO> hostIdsWithGuestOsRule = _detailsDao.findByName(Host.GUEST_OS_RULE);
|
||||
List<HostVO> hostsWithIncompatibleRules = new ArrayList<>();
|
||||
for (DetailVO guestOsRule : hostIdsWithGuestOsRule) {
|
||||
if (!GenericRuleHelper.interpretGuestOsRule(guestOsRule.getValue(), templateGuestOSName, HostDao.guestOsRuleExecutionTimeout.value(),
|
||||
HostDao.guestOsRuleExecutionTimeout.key())) {
|
||||
logger.trace("The guest OS rule [{}] of the host with ID [{}] is incompatible with the OS of the VM.",
|
||||
guestOsRule.getHostId(), guestOsRule.getValue());
|
||||
hostsWithIncompatibleRules.add(findById(guestOsRule.getHostId()));
|
||||
}
|
||||
}
|
||||
logger.trace("The hosts with the following IDs [{}] are incompatible with the VM considering their guest OS rule.",
|
||||
hostsWithIncompatibleRules);
|
||||
return hostsWithIncompatibleRules;
|
||||
}
|
||||
|
||||
public List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags) {
|
||||
Set<Long> result = new HashSet<>();
|
||||
List<HostVO> hosts = findHostsWithTagRuleThatMatchComputeOfferingTags(computeOfferingTags);
|
||||
|
|
@ -1984,4 +2005,14 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
|
|||
|
||||
return customSearch(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[] {guestOsRuleExecutionTimeout};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return HostDaoImpl.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,6 +150,12 @@ FROM `cloud`.`configuration` `cfg`
|
|||
WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.cpu.dynamic.scaling.capacity')
|
||||
AND `cfg`.`name` = 'vm.serviceoffering.cpu.cores.max';
|
||||
|
||||
-- Remove stale realhostip.com default values; domain has been dead since ~2015.
|
||||
UPDATE `cloud`.`configuration`
|
||||
SET value = NULL
|
||||
WHERE name IN ('consoleproxy.url.domain', 'secstorage.ssl.cert.domain')
|
||||
AND value IN ('realhostip.com', '*.realhostip.com');
|
||||
|
||||
-- Add management_server_details table to allow ManagementServer scope configs
|
||||
CREATE TABLE IF NOT EXISTS `management_server_details` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
|
|
@ -197,6 +203,12 @@ CREATE TABLE IF NOT EXISTS `cloud`.`image_transfer`(
|
|||
INDEX `i_image_transfer__backup_id`(`backup_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
--- Quota resource statement
|
||||
INSERT INTO cloud.role_permissions (uuid, role_id, rule, permission, sort_order)
|
||||
SELECT uuid(), role_id, 'quotaResourceStatement', permission, sort_order
|
||||
FROM cloud.role_permissions rp
|
||||
WHERE rule = 'quotaStatement' AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaResourceStatement');
|
||||
|
||||
-- ======================================================================
|
||||
-- DNS Framework Schema
|
||||
-- ======================================================================
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ SELECT
|
|||
guest_os_category.id guest_os_category_id,
|
||||
guest_os_category.uuid guest_os_category_uuid,
|
||||
guest_os_category.name guest_os_category_name,
|
||||
(SELECT `value` FROM `cloud`.`host_details` `hd` WHERE `hd`.`host_id` = `cloud`.`host`.`id` AND `hd`.`name` = 'guest.os.rule') AS `guest_os_rule`,
|
||||
mem_caps.used_capacity memory_used_capacity,
|
||||
mem_caps.reserved_capacity memory_reserved_capacity,
|
||||
cpu_caps.used_capacity cpu_used_capacity,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import java.util.Arrays;
|
|||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -32,6 +33,9 @@ import java.util.stream.Collectors;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.quota.activationrule.presetvariables.Configuration;
|
||||
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
|
||||
|
|
@ -43,9 +47,11 @@ import org.apache.cloudstack.quota.constant.QuotaTypes;
|
|||
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaTariffUsageDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
|
||||
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
|
||||
import org.apache.cloudstack.usage.UsageUnitTypes;
|
||||
|
|
@ -86,7 +92,8 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
|||
private QuotaBalanceDao _quotaBalanceDao;
|
||||
@Inject
|
||||
private ConfigurationDao _configDao;
|
||||
|
||||
@Inject
|
||||
private QuotaTariffUsageDao quotaTariffUsageDao;
|
||||
@Inject
|
||||
protected PresetVariableHelper presetVariableHelper;
|
||||
|
||||
|
|
@ -311,14 +318,14 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
|||
String accountToString = account.reflectionToString();
|
||||
logger.info("Calculating quota usage of [{}] usage records for account [{}].", usageRecords.size(), accountToString);
|
||||
|
||||
List<Pair<UsageVO, QuotaUsageVO>> pairsUsageAndQuotaUsage = new ArrayList<>();
|
||||
Map<UsageVO, Pair<QuotaUsageVO, List<QuotaTariffUsageVO>>> mapUsageAndQuotaUsage = new LinkedHashMap<>();
|
||||
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value())) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value(), QuotaConfig.QuotaActivationRuleTimeout.key())) {
|
||||
for (UsageVO usageRecord : usageRecords) {
|
||||
int usageType = usageRecord.getUsageType();
|
||||
|
||||
if (!shouldCalculateUsageRecord(account, usageRecord)) {
|
||||
pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, null));
|
||||
mapUsageAndQuotaUsage.put(usageRecord, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -326,18 +333,31 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
|||
List<QuotaTariffVO> quotaTariffs = pairQuotaTariffsPerUsageTypeAndHasActivationRule.first();
|
||||
boolean hasAnyQuotaTariffWithActivationRule = pairQuotaTariffsPerUsageTypeAndHasActivationRule.second();
|
||||
|
||||
BigDecimal aggregatedQuotaTariffsValue = aggregateQuotaTariffsValues(usageRecord, quotaTariffs, hasAnyQuotaTariffWithActivationRule, jsInterpreter, accountToString);
|
||||
Map<QuotaTariffVO, BigDecimal> aggregatedQuotaTariffsAndValues = aggregateQuotaTariffsValues(usageRecord,
|
||||
quotaTariffs, hasAnyQuotaTariffWithActivationRule, jsInterpreter, accountToString);
|
||||
BigDecimal aggregatedQuotaTariffsValue = aggregatedQuotaTariffsAndValues.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
logger.debug("The aggregation of the quota tariffs of account [{}] resulted in [{}] for the usage record [{}].",
|
||||
account, aggregatedQuotaTariffsValue, usageRecord);
|
||||
|
||||
QuotaUsageVO quotaUsage = createQuotaUsageAccordingToUsageUnit(usageRecord, aggregatedQuotaTariffsValue, accountToString);
|
||||
if (quotaUsage == null) {
|
||||
mapUsageAndQuotaUsage.put(usageRecord, null);
|
||||
continue;
|
||||
}
|
||||
|
||||
pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, quotaUsage));
|
||||
List<QuotaTariffUsageVO> quotaTariffUsages = new ArrayList<>();
|
||||
for (Map.Entry<QuotaTariffVO, BigDecimal> entry : aggregatedQuotaTariffsAndValues.entrySet()) {
|
||||
QuotaTariffUsageVO quotaTariffUsage = createQuotaTariffUsage(usageRecord, entry.getKey(), entry.getValue());
|
||||
quotaTariffUsages.add(quotaTariffUsage);
|
||||
}
|
||||
mapUsageAndQuotaUsage.put(usageRecord, new Pair<>(quotaUsage, quotaTariffUsages));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(String.format("Failed to calculate the quota usage for account [%s] due to [%s].", accountToString, e.getMessage()), e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(pairsUsageAndQuotaUsage);
|
||||
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<List<QuotaUsageVO>>) status -> persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(mapUsageAndQuotaUsage));
|
||||
}
|
||||
|
||||
protected boolean shouldCalculateUsageRecord(AccountVO accountVO, UsageVO usageRecord) {
|
||||
|
|
@ -349,31 +369,41 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected List<QuotaUsageVO> persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(List<Pair<UsageVO, QuotaUsageVO>> pairsUsageAndQuotaUsage) {
|
||||
protected List<QuotaUsageVO> persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(Map<UsageVO, Pair<QuotaUsageVO, List<QuotaTariffUsageVO>>> mapUsageAndQuotaTariffUsage) {
|
||||
List<QuotaUsageVO> quotaUsages = new ArrayList<>();
|
||||
|
||||
for (Pair<UsageVO, QuotaUsageVO> pairUsageAndQuotaUsage : pairsUsageAndQuotaUsage) {
|
||||
UsageVO usageVo = pairUsageAndQuotaUsage.first();
|
||||
for (Map.Entry<UsageVO, Pair<QuotaUsageVO, List<QuotaTariffUsageVO>>> usageAndTariffUsage : mapUsageAndQuotaTariffUsage.entrySet()) {
|
||||
UsageVO usageVo = usageAndTariffUsage.getKey();
|
||||
usageVo.setQuotaCalculated(1);
|
||||
_usageDao.persistUsage(usageVo);
|
||||
|
||||
QuotaUsageVO quotaUsageVo = pairUsageAndQuotaUsage.second();
|
||||
if (quotaUsageVo != null) {
|
||||
_quotaUsageDao.persistQuotaUsage(quotaUsageVo);
|
||||
quotaUsages.add(quotaUsageVo);
|
||||
Pair<QuotaUsageVO, List<QuotaTariffUsageVO>> pairUsageAndTariffUsages = usageAndTariffUsage.getValue();
|
||||
if (pairUsageAndTariffUsages != null) {
|
||||
QuotaUsageVO quotaUsage = pairUsageAndTariffUsages.first();
|
||||
_quotaUsageDao.persistQuotaUsage(quotaUsage);
|
||||
quotaUsages.add(quotaUsage);
|
||||
|
||||
persistQuotaTariffUsages(pairUsageAndTariffUsages.second(), quotaUsage.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return quotaUsages;
|
||||
}
|
||||
|
||||
protected BigDecimal aggregateQuotaTariffsValues(UsageVO usageRecord, List<QuotaTariffVO> quotaTariffs, boolean hasAnyQuotaTariffWithActivationRule,
|
||||
JsInterpreter jsInterpreter, String accountToString) {
|
||||
protected void persistQuotaTariffUsages(List<QuotaTariffUsageVO> quotaTariffUsages, Long quotaUsageId) {
|
||||
for (QuotaTariffUsageVO quotaTariffUsage : quotaTariffUsages) {
|
||||
quotaTariffUsage.setQuotaUsageId(quotaUsageId);
|
||||
quotaTariffUsageDao.persistQuotaTariffUsage(quotaTariffUsage);
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<QuotaTariffVO, BigDecimal> aggregateQuotaTariffsValues(UsageVO usageRecord, List<QuotaTariffVO> quotaTariffs, boolean hasAnyQuotaTariffWithActivationRule,
|
||||
JsInterpreter jsInterpreter, String accountToString) {
|
||||
String usageRecordToString = usageRecord.toString(usageAggregationTimeZone);
|
||||
logger.debug("Validating usage record [{}] for account [{}] against [{}] quota tariffs.", usageRecordToString, accountToString, quotaTariffs.size());
|
||||
|
||||
PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord);
|
||||
BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO;
|
||||
Map<QuotaTariffVO, BigDecimal> aggregatedQuotaTariffsAndValues = new HashMap<>();
|
||||
|
||||
quotaTariffs.sort(Comparator.comparing(QuotaTariffVO::getPosition));
|
||||
|
||||
|
|
@ -382,10 +412,9 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
|||
|
||||
for (QuotaTariffVO quotaTariff : quotaTariffs) {
|
||||
if (isQuotaTariffInPeriodToBeApplied(usageRecord, quotaTariff, accountToString)) {
|
||||
|
||||
BigDecimal tariffValue = getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables, lastTariffs);
|
||||
|
||||
aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(tariffValue);
|
||||
aggregatedQuotaTariffsAndValues.put(quotaTariff, tariffValue);
|
||||
|
||||
Tariff tariffPresetVariable = new Tariff();
|
||||
tariffPresetVariable.setId(quotaTariff.getUuid());
|
||||
|
|
@ -394,10 +423,10 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
|||
}
|
||||
}
|
||||
|
||||
logger.debug(String.format("The aggregation of the quota tariffs resulted in the value [%s] for the usage record [%s]. We will use this value to calculate the final"
|
||||
+ " usage value.", aggregatedQuotaTariffsValue, usageRecordToString));
|
||||
logger.debug("The aggregation of the quota tariffs resulted in [{}] quota tariffs for the usage record [{}]. The values of the quota tariffs will be used"
|
||||
+ " to calculate the final usage value.", aggregatedQuotaTariffsAndValues.size(), usageRecordToString);
|
||||
|
||||
return aggregatedQuotaTariffsValue;
|
||||
return aggregatedQuotaTariffsAndValues;
|
||||
}
|
||||
|
||||
protected PresetVariables getPresetVariables(boolean hasAnyQuotaTariffWithActivationRule, UsageVO usageRecord) {
|
||||
|
|
@ -408,6 +437,26 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected QuotaTariffUsageVO createQuotaTariffUsage(UsageVO usageRecord, QuotaTariffVO quotaTariff, BigDecimal quotaTariffValue) {
|
||||
BigDecimal quotaUsageValue = BigDecimal.ZERO;
|
||||
|
||||
if (!quotaTariffValue.equals(BigDecimal.ZERO)) {
|
||||
String quotaUnit = QuotaTypes.getQuotaType(usageRecord.getUsageType()).getQuotaUnit();
|
||||
logger.trace("Calculating the value of the quota tariff [{}] according to its value [{}] and its quota unit [{}].", quotaTariff, quotaTariffValue, quotaUnit);
|
||||
quotaUsageValue = getUsageValueAccordingToUsageUnitType(usageRecord, quotaTariffValue, quotaUnit);
|
||||
logger.debug("The calculation of the value of the quota tariff [{}] according to its value [{}] and its usage unit [{}] resulted in the value [{}].",
|
||||
quotaTariff, quotaTariffValue, quotaUnit, quotaUsageValue);
|
||||
} else {
|
||||
logger.debug("Quota tariff [{}] has no value to be calculated; therefore, it will be marked as value zero.", quotaTariff);
|
||||
}
|
||||
|
||||
QuotaTariffUsageVO quotaTariffUsage = new QuotaTariffUsageVO();
|
||||
quotaTariffUsage.setTariffId(quotaTariff.getId());
|
||||
quotaTariffUsage.setQuotaUsed(quotaUsageValue);
|
||||
|
||||
return quotaTariffUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quota tariff value according to the result of the activation rule.<br/>
|
||||
* <ul>
|
||||
|
|
|
|||
|
|
@ -20,11 +20,23 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.network.IpAddress;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.VpnUser;
|
||||
import com.cloud.network.rules.LoadBalancer;
|
||||
import com.cloud.network.rules.PortForwardingRule;
|
||||
import com.cloud.network.security.SecurityGroup;
|
||||
import com.cloud.network.vpc.Vpc;
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.template.VirtualMachineTemplate;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.storage.object.Bucket;
|
||||
import org.apache.cloudstack.usage.UsageTypes;
|
||||
import org.apache.cloudstack.usage.UsageUnitTypes;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class QuotaTypes extends UsageTypes {
|
||||
private final Integer quotaType;
|
||||
|
|
@ -32,44 +44,46 @@ public class QuotaTypes extends UsageTypes {
|
|||
private final String quotaUnit;
|
||||
private final String description;
|
||||
private final String discriminator;
|
||||
private final Class<?> clazz;
|
||||
private final static Map<Integer, QuotaTypes> quotaTypeMap;
|
||||
|
||||
static {
|
||||
final HashMap<Integer, QuotaTypes> quotaTypeList = new HashMap<Integer, QuotaTypes>();
|
||||
quotaTypeList.put(RUNNING_VM, new QuotaTypes(RUNNING_VM, "RUNNING_VM", UsageUnitTypes.COMPUTE_MONTH.toString(), "Running Vm Usage"));
|
||||
quotaTypeList.put(ALLOCATED_VM, new QuotaTypes(ALLOCATED_VM, "ALLOCATED_VM", UsageUnitTypes.COMPUTE_MONTH.toString(), "Allocated Vm Usage"));
|
||||
quotaTypeList.put(IP_ADDRESS, new QuotaTypes(IP_ADDRESS, "IP_ADDRESS", UsageUnitTypes.IP_MONTH.toString(), "IP Address Usage"));
|
||||
quotaTypeList.put(NETWORK_BYTES_SENT, new QuotaTypes(NETWORK_BYTES_SENT, "NETWORK_BYTES_SENT", UsageUnitTypes.GB.toString(), "Network Usage (Bytes Sent)"));
|
||||
quotaTypeList.put(NETWORK_BYTES_RECEIVED, new QuotaTypes(NETWORK_BYTES_RECEIVED, "NETWORK_BYTES_RECEIVED", UsageUnitTypes.GB.toString(), "Network Usage (Bytes Received)"));
|
||||
quotaTypeList.put(VOLUME, new QuotaTypes(VOLUME, "VOLUME", UsageUnitTypes.GB_MONTH.toString(), "Volume Usage"));
|
||||
quotaTypeList.put(TEMPLATE, new QuotaTypes(TEMPLATE, "TEMPLATE", UsageUnitTypes.GB_MONTH.toString(), "Template Usage"));
|
||||
quotaTypeList.put(ISO, new QuotaTypes(ISO, "ISO", UsageUnitTypes.GB_MONTH.toString(), "ISO Usage"));
|
||||
quotaTypeList.put(SNAPSHOT, new QuotaTypes(SNAPSHOT, "SNAPSHOT", UsageUnitTypes.GB_MONTH.toString(), "Snapshot Usage"));
|
||||
quotaTypeList.put(SECURITY_GROUP, new QuotaTypes(SECURITY_GROUP, "SECURITY_GROUP", UsageUnitTypes.POLICY_MONTH.toString(), "Security Group Usage"));
|
||||
quotaTypeList.put(LOAD_BALANCER_POLICY, new QuotaTypes(LOAD_BALANCER_POLICY, "LOAD_BALANCER_POLICY", UsageUnitTypes.POLICY_MONTH.toString(), "Load Balancer Usage"));
|
||||
quotaTypeList.put(PORT_FORWARDING_RULE, new QuotaTypes(PORT_FORWARDING_RULE, "PORT_FORWARDING_RULE", UsageUnitTypes.POLICY_MONTH.toString(), "Port Forwarding Usage"));
|
||||
quotaTypeList.put(NETWORK_OFFERING, new QuotaTypes(NETWORK_OFFERING, "NETWORK_OFFERING", UsageUnitTypes.POLICY_MONTH.toString(), "Network Offering Usage"));
|
||||
quotaTypeList.put(VPN_USERS, new QuotaTypes(VPN_USERS, "VPN_USERS", UsageUnitTypes.POLICY_MONTH.toString(), "VPN users usage"));
|
||||
quotaTypeList.put(VM_DISK_IO_READ, new QuotaTypes(VM_DISK_IO_READ, "VM_DISK_IO_READ", UsageUnitTypes.IOPS.toString(), "VM Disk usage(I/O Read)"));
|
||||
quotaTypeList.put(VM_DISK_IO_WRITE, new QuotaTypes(VM_DISK_IO_WRITE, "VM_DISK_IO_WRITE", UsageUnitTypes.IOPS.toString(), "VM Disk usage(I/O Write)"));
|
||||
quotaTypeList.put(VM_DISK_BYTES_READ, new QuotaTypes(VM_DISK_BYTES_READ, "VM_DISK_BYTES_READ", UsageUnitTypes.BYTES.toString(), "VM Disk usage(Bytes Read)"));
|
||||
quotaTypeList.put(VM_DISK_BYTES_WRITE, new QuotaTypes(VM_DISK_BYTES_WRITE, "VM_DISK_BYTES_WRITE", UsageUnitTypes.BYTES.toString(), "VM Disk usage(Bytes Write)"));
|
||||
quotaTypeList.put(VM_SNAPSHOT, new QuotaTypes(VM_SNAPSHOT, "VM_SNAPSHOT", UsageUnitTypes.GB_MONTH.toString(), "VM Snapshot storage usage"));
|
||||
quotaTypeList.put(VOLUME_SECONDARY, new QuotaTypes(VOLUME_SECONDARY, "VOLUME_SECONDARY", UsageUnitTypes.GB_MONTH.toString(), "Volume secondary storage usage"));
|
||||
quotaTypeList.put(VM_SNAPSHOT_ON_PRIMARY, new QuotaTypes(VM_SNAPSHOT_ON_PRIMARY, "VM_SNAPSHOT_ON_PRIMARY", UsageUnitTypes.GB_MONTH.toString(), "VM Snapshot primary storage usage"));
|
||||
quotaTypeList.put(BACKUP, new QuotaTypes(BACKUP, "BACKUP", UsageUnitTypes.GB_MONTH.toString(), "Backup storage usage"));
|
||||
quotaTypeList.put(BUCKET, new QuotaTypes(BUCKET, "BUCKET", UsageUnitTypes.GB_MONTH.toString(), "Object Store bucket usage"));
|
||||
quotaTypeList.put(NETWORK, new QuotaTypes(NETWORK, "NETWORK", UsageUnitTypes.COMPUTE_MONTH.toString(), "Network usage"));
|
||||
quotaTypeList.put(VPC, new QuotaTypes(VPC, "VPC", UsageUnitTypes.COMPUTE_MONTH.toString(), "VPC usage"));
|
||||
quotaTypeList.put(RUNNING_VM, new QuotaTypes(RUNNING_VM, "RUNNING_VM", UsageUnitTypes.COMPUTE_MONTH.toString(), "Running Vm Usage", VirtualMachine.class));
|
||||
quotaTypeList.put(ALLOCATED_VM, new QuotaTypes(ALLOCATED_VM, "ALLOCATED_VM", UsageUnitTypes.COMPUTE_MONTH.toString(), "Allocated Vm Usage", VirtualMachine.class));
|
||||
quotaTypeList.put(IP_ADDRESS, new QuotaTypes(IP_ADDRESS, "IP_ADDRESS", UsageUnitTypes.IP_MONTH.toString(), "IP Address Usage", IpAddress.class));
|
||||
quotaTypeList.put(NETWORK_BYTES_SENT, new QuotaTypes(NETWORK_BYTES_SENT, "NETWORK_BYTES_SENT", UsageUnitTypes.GB.toString(), "Network Usage (Bytes Sent)", Network.class));
|
||||
quotaTypeList.put(NETWORK_BYTES_RECEIVED, new QuotaTypes(NETWORK_BYTES_RECEIVED, "NETWORK_BYTES_RECEIVED", UsageUnitTypes.GB.toString(), "Network Usage (Bytes Received)", Network.class));
|
||||
quotaTypeList.put(VOLUME, new QuotaTypes(VOLUME, "VOLUME", UsageUnitTypes.GB_MONTH.toString(), "Volume Usage", Volume.class));
|
||||
quotaTypeList.put(TEMPLATE, new QuotaTypes(TEMPLATE, "TEMPLATE", UsageUnitTypes.GB_MONTH.toString(), "Template Usage", VirtualMachineTemplate.class));
|
||||
quotaTypeList.put(ISO, new QuotaTypes(ISO, "ISO", UsageUnitTypes.GB_MONTH.toString(), "ISO Usage", VirtualMachineTemplate.class));
|
||||
quotaTypeList.put(SNAPSHOT, new QuotaTypes(SNAPSHOT, "SNAPSHOT", UsageUnitTypes.GB_MONTH.toString(), "Snapshot Usage", Snapshot.class));
|
||||
quotaTypeList.put(SECURITY_GROUP, new QuotaTypes(SECURITY_GROUP, "SECURITY_GROUP", UsageUnitTypes.POLICY_MONTH.toString(), "Security Group Usage", SecurityGroup.class));
|
||||
quotaTypeList.put(LOAD_BALANCER_POLICY, new QuotaTypes(LOAD_BALANCER_POLICY, "LOAD_BALANCER_POLICY", UsageUnitTypes.POLICY_MONTH.toString(), "Load Balancer Usage", LoadBalancer.class));
|
||||
quotaTypeList.put(PORT_FORWARDING_RULE, new QuotaTypes(PORT_FORWARDING_RULE, "PORT_FORWARDING_RULE", UsageUnitTypes.POLICY_MONTH.toString(), "Port Forwarding Usage", PortForwardingRule.class));
|
||||
quotaTypeList.put(NETWORK_OFFERING, new QuotaTypes(NETWORK_OFFERING, "NETWORK_OFFERING", UsageUnitTypes.POLICY_MONTH.toString(), "Network Offering Usage", NetworkOffering.class));
|
||||
quotaTypeList.put(VPN_USERS, new QuotaTypes(VPN_USERS, "VPN_USERS", UsageUnitTypes.POLICY_MONTH.toString(), "VPN users usage", VpnUser.class));
|
||||
quotaTypeList.put(VM_DISK_IO_READ, new QuotaTypes(VM_DISK_IO_READ, "VM_DISK_IO_READ", UsageUnitTypes.IOPS.toString(), "VM Disk usage(I/O Read)", Volume.class));
|
||||
quotaTypeList.put(VM_DISK_IO_WRITE, new QuotaTypes(VM_DISK_IO_WRITE, "VM_DISK_IO_WRITE", UsageUnitTypes.IOPS.toString(), "VM Disk usage(I/O Write)", Volume.class));
|
||||
quotaTypeList.put(VM_DISK_BYTES_READ, new QuotaTypes(VM_DISK_BYTES_READ, "VM_DISK_BYTES_READ", UsageUnitTypes.BYTES.toString(), "VM Disk usage(Bytes Read)", Volume.class));
|
||||
quotaTypeList.put(VM_DISK_BYTES_WRITE, new QuotaTypes(VM_DISK_BYTES_WRITE, "VM_DISK_BYTES_WRITE", UsageUnitTypes.BYTES.toString(), "VM Disk usage(Bytes Write)", Volume.class));
|
||||
quotaTypeList.put(VM_SNAPSHOT, new QuotaTypes(VM_SNAPSHOT, "VM_SNAPSHOT", UsageUnitTypes.GB_MONTH.toString(), "VM Snapshot storage usage", Snapshot.class));
|
||||
quotaTypeList.put(VOLUME_SECONDARY, new QuotaTypes(VOLUME_SECONDARY, "VOLUME_SECONDARY", UsageUnitTypes.GB_MONTH.toString(), "Volume secondary storage usage", Volume.class));
|
||||
quotaTypeList.put(VM_SNAPSHOT_ON_PRIMARY, new QuotaTypes(VM_SNAPSHOT_ON_PRIMARY, "VM_SNAPSHOT_ON_PRIMARY", UsageUnitTypes.GB_MONTH.toString(), "VM Snapshot primary storage usage", Snapshot.class));
|
||||
quotaTypeList.put(BACKUP, new QuotaTypes(BACKUP, "BACKUP", UsageUnitTypes.GB_MONTH.toString(), "Backup storage usage", BackupOffering.class));
|
||||
quotaTypeList.put(BUCKET, new QuotaTypes(BUCKET, "BUCKET", UsageUnitTypes.GB_MONTH.toString(), "Object Store bucket usage", Bucket.class));
|
||||
quotaTypeList.put(NETWORK, new QuotaTypes(NETWORK, "NETWORK", UsageUnitTypes.COMPUTE_MONTH.toString(), "Network usage", Network.class));
|
||||
quotaTypeList.put(VPC, new QuotaTypes(VPC, "VPC", UsageUnitTypes.COMPUTE_MONTH.toString(), "VPC usage", Vpc.class));
|
||||
quotaTypeMap = Collections.unmodifiableMap(quotaTypeList);
|
||||
}
|
||||
|
||||
private QuotaTypes(Integer quotaType, String name, String unit, String description) {
|
||||
private QuotaTypes(Integer quotaType, String name, String unit, String description, Class<?> clazz) {
|
||||
this.quotaType = quotaType;
|
||||
this.description = description;
|
||||
this.quotaName = name;
|
||||
this.quotaUnit = unit;
|
||||
this.discriminator = "None";
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public static Map<Integer, QuotaTypes> listQuotaTypes() {
|
||||
|
|
@ -104,22 +118,20 @@ public class QuotaTypes extends UsageTypes {
|
|||
return null;
|
||||
}
|
||||
|
||||
public Class<?> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
static public QuotaTypes getQuotaType(int quotaType) {
|
||||
return quotaTypeMap.get(quotaType);
|
||||
}
|
||||
|
||||
static public QuotaTypes getQuotaTypeByName(String name) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
throw new CloudRuntimeException("Could not retrieve Quota type by name because the value passed as parameter is null, empty, or blank.");
|
||||
static public Class<?> getClazz(int quotaType) {
|
||||
QuotaTypes t = quotaTypeMap.get(quotaType);
|
||||
if (t != null) {
|
||||
return t.getClazz();
|
||||
}
|
||||
|
||||
for (QuotaTypes type : quotaTypeMap.values()) {
|
||||
if (type.getQuotaName().equals(name)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
throw new CloudRuntimeException(String.format("Could not find Quota type with name [%s].", name));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -26,6 +26,6 @@ import java.util.List;
|
|||
|
||||
public interface QuotaUsageJoinDao extends GenericDao<QuotaUsageJoinVO, Long> {
|
||||
|
||||
List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, Long domainId, Integer usageType, Long resourceId, Long networkId, Long offeringId, Date startDate, Date endDate, Long tariffId);
|
||||
List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, List<Long> domainIds, Integer usageType, Long resourceId, Long networkId, Long offeringId, Date startDate, Date endDate, Long tariffId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public class QuotaUsageJoinDaoImpl extends GenericDaoBase<QuotaUsageJoinVO, Long
|
|||
|
||||
private void prepareQuotaUsageSearchBuilder(SearchBuilder<QuotaUsageJoinVO> searchBuilder) {
|
||||
searchBuilder.and("accountId", searchBuilder.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||
searchBuilder.and("domainId", searchBuilder.entity().getDomainId(), SearchCriteria.Op.EQ);
|
||||
searchBuilder.and("domainIds", searchBuilder.entity().getDomainId(), SearchCriteria.Op.IN);
|
||||
searchBuilder.and("usageType", searchBuilder.entity().getUsageType(), SearchCriteria.Op.EQ);
|
||||
searchBuilder.and("resourceId", searchBuilder.entity().getResourceId(), SearchCriteria.Op.EQ);
|
||||
searchBuilder.and("networkId", searchBuilder.entity().getNetworkId(), SearchCriteria.Op.EQ);
|
||||
|
|
@ -71,11 +71,13 @@ public class QuotaUsageJoinDaoImpl extends GenericDaoBase<QuotaUsageJoinVO, Long
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, Long domainId, Integer usageType, Long resourceId, Long networkId, Long offeringId, Date startDate, Date endDate, Long tariffId) {
|
||||
public List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, List<Long> domainIds, Integer usageType, Long resourceId, Long networkId, Long offeringId, Date startDate, Date endDate, Long tariffId) {
|
||||
SearchCriteria<QuotaUsageJoinVO> sc = tariffId == null ? searchQuotaUsages.create() : searchQuotaUsagesJoinTariffUsages.create();
|
||||
|
||||
sc.setParametersIfNotNull("accountId", accountId);
|
||||
sc.setParametersIfNotNull("domainId", domainId);
|
||||
if (domainIds != null) {
|
||||
sc.setParameters("domainIds", domainIds.toArray());
|
||||
}
|
||||
sc.setParametersIfNotNull("usageType", usageType);
|
||||
sc.setParametersIfNotNull("resourceId", resourceId);
|
||||
sc.setParametersIfNotNull("networkId", networkId);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.text.SimpleDateFormat;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -33,7 +34,9 @@ import org.apache.cloudstack.quota.activationrule.presetvariables.Tariff;
|
|||
import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
|
||||
import org.apache.cloudstack.quota.constant.QuotaTypes;
|
||||
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaTariffUsageDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
|
||||
import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
|
||||
import org.apache.cloudstack.usage.UsageUnitTypes;
|
||||
|
|
@ -89,6 +92,9 @@ public class QuotaManagerImplTest {
|
|||
@Mock
|
||||
PresetVariables presetVariablesMock;
|
||||
|
||||
@Mock
|
||||
QuotaTariffUsageDao quotaTariffUsageDaoMock;
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Test
|
||||
|
|
@ -484,47 +490,49 @@ public class QuotaManagerImplTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void aggregateQuotaTariffsValuesTestTariffsWereNotInPeriodToBeAppliedReturnZero() {
|
||||
public void aggregateQuotaTariffsValuesTestTariffsWereNotInPeriodToBeAppliedReturnEmptyMap() {
|
||||
List<QuotaTariffVO> tariffs = createTariffList();
|
||||
|
||||
Mockito.doReturn(false).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
|
||||
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
|
||||
Map<QuotaTariffVO, BigDecimal> result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
|
||||
|
||||
Assert.assertEquals(BigDecimal.ZERO, result);
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregateQuotaTariffsValuesTestTariffsIsEmptyReturnZero() {
|
||||
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, new ArrayList<>(), false, jsInterpreterMock, "");
|
||||
public void aggregateQuotaTariffsValuesTestTariffsIsEmptyReturnEmptyMap() {
|
||||
Map<QuotaTariffVO, BigDecimal> result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, new ArrayList<>(), false, jsInterpreterMock, "");
|
||||
|
||||
Assert.assertEquals(BigDecimal.ZERO, result);
|
||||
Assert.assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aggregateQuotaTariffsValuesTestTariffsAreInPeriodToBeAppliedReturnAggregation() {
|
||||
public void aggregateQuotaTariffsValuesTestTariffsAreInPeriodToBeAppliedReturnTariffsWithTheirValues() {
|
||||
List<QuotaTariffVO> tariffs = createTariffList();
|
||||
|
||||
Mockito.doReturn(true, false, true).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
|
||||
Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getQuotaTariffValueToBeApplied(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
|
||||
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
|
||||
Map<QuotaTariffVO, BigDecimal> result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
|
||||
|
||||
Assert.assertEquals(BigDecimal.TEN.multiply(new BigDecimal(2)), result);
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertTrue(result.values().stream().allMatch(value -> value.equals(BigDecimal.TEN)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsagesTestReturnOnlyPersistedQuotaUsageVo() {
|
||||
List<Pair<UsageVO, QuotaUsageVO>> listPair = new ArrayList<>();
|
||||
Map<UsageVO, Pair<QuotaUsageVO, List<QuotaTariffUsageVO>>> mapUsageQuotaUsage = new LinkedHashMap<>();
|
||||
QuotaUsageVO quotaUsageVoMock1 = Mockito.mock(QuotaUsageVO.class);
|
||||
QuotaUsageVO quotaUsageVoMock2 = Mockito.mock(QuotaUsageVO.class);
|
||||
|
||||
listPair.add(new Pair<>(new UsageVO(), quotaUsageVoMock1));
|
||||
listPair.add(new Pair<>(new UsageVO(), null));
|
||||
listPair.add(new Pair<>(new UsageVO(), quotaUsageVoMock2));
|
||||
mapUsageQuotaUsage.put(new UsageVO(), new Pair<>(quotaUsageVoMock1, new ArrayList<>()));
|
||||
mapUsageQuotaUsage.put(new UsageVO(), null);
|
||||
mapUsageQuotaUsage.put(new UsageVO(), new Pair<>(quotaUsageVoMock2, new ArrayList<>()));
|
||||
|
||||
Mockito.doReturn(null).when(usageDaoMock).persistUsage(Mockito.any());
|
||||
Mockito.doReturn(null).when(quotaUsageDaoMock).persistQuotaUsage(Mockito.any());
|
||||
Mockito.doNothing().when(quotaManagerImplSpy).persistQuotaTariffUsages(Mockito.any(), Mockito.any());
|
||||
|
||||
List<QuotaUsageVO> result = quotaManagerImplSpy.persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(listPair);
|
||||
List<QuotaUsageVO> result = quotaManagerImplSpy.persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsages(mapUsageQuotaUsage);
|
||||
|
||||
Assert.assertEquals(2, result.size());
|
||||
Assert.assertEquals(quotaUsageVoMock1, result.get(0));
|
||||
|
|
@ -551,4 +559,41 @@ public class QuotaManagerImplTest {
|
|||
return lastTariffs;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createQuotaTariffUsageTestTariffValueIsNotZeroReturnDetailedVo() {
|
||||
Mockito.doReturn(1).when(usageVoMock).getUsageType();
|
||||
Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getUsageValueAccordingToUsageUnitType(Mockito.any(), Mockito.any(), Mockito.any());
|
||||
Mockito.doReturn(1L).when(quotaTariffVoMock).getId();
|
||||
|
||||
QuotaTariffUsageVO result = quotaManagerImplSpy.createQuotaTariffUsage(usageVoMock, quotaTariffVoMock, BigDecimal.TEN);
|
||||
|
||||
Assert.assertEquals(1L, result.getTariffId().longValue());
|
||||
Assert.assertEquals(BigDecimal.TEN, result.getQuotaUsed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistQuotaTariffUsagesTestZeroQuotaTariffUsagesZeroPersisted() {
|
||||
List<QuotaTariffUsageVO> quotaTariffUsages = new ArrayList<>();
|
||||
|
||||
quotaManagerImplSpy.persistQuotaTariffUsages(quotaTariffUsages, 1L);
|
||||
|
||||
Mockito.verify(quotaTariffUsageDaoMock, Mockito.never()).persistQuotaTariffUsage(Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistQuotaTariffUsagesTestTwoQuotaUsageDetailsTwoPersisted() {
|
||||
List<QuotaTariffUsageVO> quotaUsageDetails = new ArrayList<>();
|
||||
QuotaTariffUsageVO quotaUsageDetailVoMock1 = Mockito.mock(QuotaTariffUsageVO.class);
|
||||
QuotaTariffUsageVO quotaUsageDetailVoMock2 = Mockito.mock(QuotaTariffUsageVO.class);
|
||||
|
||||
quotaUsageDetails.add(quotaUsageDetailVoMock1);
|
||||
quotaUsageDetails.add(quotaUsageDetailVoMock2);
|
||||
|
||||
Mockito.doNothing().when(quotaTariffUsageDaoMock).persistQuotaTariffUsage(Mockito.any());
|
||||
|
||||
quotaManagerImplSpy.persistQuotaTariffUsages(quotaUsageDetails, 1L);
|
||||
|
||||
Mockito.verify(quotaTariffUsageDaoMock, Mockito.times(2)).persistQuotaTariffUsage(Mockito.any());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
//Licensed to the Apache Software Foundation (ASF) under one
|
||||
//or more contributor license agreements. See the NOTICE file
|
||||
//distributed with this work for additional information
|
||||
//regarding copyright ownership. The ASF licenses this file
|
||||
//to you under the Apache License, Version 2.0 (the
|
||||
//"License"); you may not use this file except in compliance
|
||||
//with the License. You may obtain a copy of the License at
|
||||
//
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing,
|
||||
//software distributed under the License is distributed on an
|
||||
//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
//KIND, either express or implied. See the License for the
|
||||
//specific language governing permissions and limitations
|
||||
//under the License.
|
||||
package org.apache.cloudstack.api.command;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.AccountResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
|
||||
import org.apache.cloudstack.api.response.QuotaResourceStatementResponse;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@APICommand(name = "quotaResourceStatement", responseObject = QuotaResourceStatementResponse.class, since = "4.23.0.0",
|
||||
description = "Generates a detailed Quota statement for a specific resource.", requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false)
|
||||
public class QuotaResourceStatementCmd extends BaseCmd {
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.STRING, required = true, description = "ID of the resource.")
|
||||
private String id;
|
||||
|
||||
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class,
|
||||
description = "Generate the statement for this Account. A resource may have belonged to different owners at distinct points in time, " +
|
||||
"so this parameter can be used to only consider the period for which it belonged to a specific Account.")
|
||||
private Long accountId;
|
||||
|
||||
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class,
|
||||
description = "Generate the statement for this Project. A resource may have belonged to different owners at distinct points in time, " +
|
||||
"so this parameter can be used to only consider the period for which it belonged to a specific Project.")
|
||||
private Long projectId;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class,
|
||||
description = "ID of the Domain for which the Quota statement will be generated.")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = true, description = "Usage type of the Quota usage.")
|
||||
private Integer usageType;
|
||||
|
||||
@Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = true, description = "Start date for the query. " +
|
||||
ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS)
|
||||
private Date startDate;
|
||||
|
||||
@Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = true, description = "End date for the query. " +
|
||||
ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS)
|
||||
private Date endDate;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, description = "Whether to include usage records from children of the filtered domain. "
|
||||
+ " Defaults to false.")
|
||||
private Boolean recursive;
|
||||
|
||||
@Inject
|
||||
QuotaResponseBuilder responseBuilder;
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
QuotaResourceStatementResponse response = responseBuilder.createQuotaResourceStatement(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
if (ObjectUtils.allNull(accountId, projectId)) {
|
||||
return -1;
|
||||
}
|
||||
return _accountService.finalizeAccountId(accountId, null, null, projectId);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Integer getUsageType() {
|
||||
return usageType;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public boolean isRecursive() {
|
||||
return BooleanUtils.isTrue(recursive);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ import java.util.Date;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.api.ACL;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
|
|
@ -32,18 +31,17 @@ import org.apache.cloudstack.api.response.QuotaResponseBuilder;
|
|||
import org.apache.cloudstack.api.response.QuotaStatementItemResponse;
|
||||
import org.apache.cloudstack.api.response.QuotaStatementResponse;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
@APICommand(name = "quotaStatement", responseObject = QuotaStatementItemResponse.class, description = "Create a Quota statement for the provided Account, Project, or Domain.",
|
||||
since = "4.7.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, httpMethod = "GET")
|
||||
public class QuotaStatementCmd extends BaseCmd {
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING,
|
||||
description = "Name of the Account for which the Quota statement will be generated. Deprecated, please use accountid instead.")
|
||||
private String accountName;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class,
|
||||
description = "ID of the Domain for which the Quota statement will be generated. May be used individually or with account.")
|
||||
private Long domainId;
|
||||
|
|
@ -60,16 +58,18 @@ public class QuotaStatementCmd extends BaseCmd {
|
|||
description = "Consider only Quota usage records for the specified usage type in the statement.")
|
||||
private Integer usageType;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class,
|
||||
description = "ID of the Account for which the Quota statement will be generated. Can not be specified with projectid.")
|
||||
private Long accountId;
|
||||
|
||||
@ACL
|
||||
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class,
|
||||
description = "ID of the Project for which the Quota statement will be generated. Can not be specified with accountid.", since = "4.23.0")
|
||||
private Long projectId;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, description = "Whether to include usage records from children of the filtered domain. "
|
||||
+ " Defaults to false.")
|
||||
private Boolean recursive;
|
||||
|
||||
@Parameter(name = ApiConstants.SHOW_RESOURCES, type = CommandType.BOOLEAN, description = "List the resources of each Quota type in the period.", since = "4.23.0")
|
||||
private boolean showResources;
|
||||
|
||||
|
|
@ -152,4 +152,9 @@ public class QuotaStatementCmd extends BaseCmd {
|
|||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
public boolean isRecursive() {
|
||||
return BooleanUtils.isTrue(recursive);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
//Licensed to the Apache Software Foundation (ASF) under one
|
||||
//or more contributor license agreements. See the NOTICE file
|
||||
//distributed with this work for additional information
|
||||
//regarding copyright ownership. The ASF licenses this file
|
||||
//to you under the Apache License, Version 2.0 (the
|
||||
//"License"); you may not use this file except in compliance
|
||||
//with the License. You may obtain a copy of the License at
|
||||
//
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing,
|
||||
//software distributed under the License is distributed on an
|
||||
//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
//KIND, either express or implied. See the License for the
|
||||
//specific language governing permissions and limitations
|
||||
//under the License.
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
|
||||
public class QuotaResourceStatementItemResponse extends BaseResponse {
|
||||
|
||||
@SerializedName(ApiConstants.TARIFF_ID)
|
||||
@Param(description = "ID of the tariff.")
|
||||
private String tariffId;
|
||||
|
||||
@SerializedName(ApiConstants.TARIFF_NAME)
|
||||
@Param(description = "Name of the tariff.")
|
||||
private String tariffName;
|
||||
|
||||
@SerializedName(ApiConstants.QUOTA_CONSUMED)
|
||||
@Param(description = "Amount of quota used.")
|
||||
private BigDecimal quotaUsed;
|
||||
|
||||
@SerializedName(ApiConstants.START_DATE)
|
||||
@Param(description = "Item's start date.")
|
||||
private Date startDate;
|
||||
|
||||
@SerializedName(ApiConstants.END_DATE)
|
||||
@Param(description = "Item's end date.")
|
||||
private Date endDate;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT_ID)
|
||||
@Param(description = "UUID of the resource's owner.")
|
||||
private String accountId;
|
||||
|
||||
public QuotaResourceStatementItemResponse() {
|
||||
super("quotaresourcestatementitem");
|
||||
}
|
||||
|
||||
public void setTariffId(String tariffId) {
|
||||
this.tariffId = tariffId;
|
||||
}
|
||||
|
||||
public void setTariffName(String tariffName) {
|
||||
this.tariffName = tariffName;
|
||||
}
|
||||
|
||||
public void setQuotaUsed(BigDecimal quotaUsed) {
|
||||
this.quotaUsed = quotaUsed;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public void setEndDate(Date endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public void setAccountId(String accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
//Licensed to the Apache Software Foundation (ASF) under one
|
||||
//or more contributor license agreements. See the NOTICE file
|
||||
//distributed with this work for additional information
|
||||
//regarding copyright ownership. The ASF licenses this file
|
||||
//to you under the Apache License, Version 2.0 (the
|
||||
//"License"); you may not use this file except in compliance
|
||||
//with the License. You may obtain a copy of the License at
|
||||
//
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing,
|
||||
//software distributed under the License is distributed on an
|
||||
//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
//KIND, either express or implied. See the License for the
|
||||
//specific language governing permissions and limitations
|
||||
//under the License.
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class QuotaResourceStatementResponse extends BaseResponse {
|
||||
|
||||
@SerializedName(ApiConstants.USAGE_NAME)
|
||||
@Param(description = "Name of the usage type.")
|
||||
private String usageName;
|
||||
|
||||
@SerializedName(ApiConstants.UNIT)
|
||||
@Param(description = "Unit of the usage type.")
|
||||
private String unit;
|
||||
|
||||
@SerializedName(ApiConstants.ITEMS)
|
||||
@Param(description = "List of Quota tariff usages.", responseObject = QuotaResourceStatementItemResponse.class)
|
||||
private List<QuotaResourceStatementItemResponse> items;
|
||||
|
||||
@SerializedName(ApiConstants.TOTAL_QUOTA)
|
||||
@Param(description = "Total amount of quota used.")
|
||||
private BigDecimal totalQuotaUsed;
|
||||
|
||||
public QuotaResourceStatementResponse() {
|
||||
super("quotaresourcestatement");
|
||||
}
|
||||
|
||||
public void setUsageName(String usageName) {
|
||||
this.usageName = usageName;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public void setQuotaUsageDetails(List<QuotaResourceStatementItemResponse> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void setTotalQuotaUsed(BigDecimal totalQuotaUsed) {
|
||||
this.totalQuotaUsed = totalQuotaUsed;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import org.apache.cloudstack.api.command.QuotaCreditsListCmd;
|
|||
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaResourceStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
|
||||
|
|
@ -83,4 +84,6 @@ public interface QuotaResponseBuilder {
|
|||
Pair<List<QuotaCreditsResponse>, Integer> createQuotaCreditsListResponse(QuotaCreditsListCmd cmd);
|
||||
|
||||
QuotaValidateActivationRuleResponse validateActivationRule(QuotaValidateActivationRuleCmd cmd);
|
||||
|
||||
QuotaResourceStatementResponse createQuotaResourceStatement(QuotaResourceStatementCmd cmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,11 +66,14 @@ import com.cloud.user.User;
|
|||
import com.cloud.user.UserVO;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
|
||||
|
|
@ -78,6 +81,7 @@ import org.apache.cloudstack.api.command.QuotaCreditsListCmd;
|
|||
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaResourceStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
|
||||
|
|
@ -107,16 +111,20 @@ import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao;
|
|||
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaSummaryDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaTariffUsageDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
|
||||
import org.apache.cloudstack.quota.dao.QuotaUsageJoinDao;
|
||||
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaSummaryVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
|
||||
import org.apache.cloudstack.quota.vo.QuotaUsageResourceVO;
|
||||
import org.apache.cloudstack.usage.UsageTypes;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
|
@ -181,7 +189,12 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
private VMTemplateDao vmTemplateDao;
|
||||
@Inject
|
||||
private VolumeDao volumeDao;
|
||||
|
||||
@Inject
|
||||
private QuotaUsageJoinDao quotaUsageJoinDao;
|
||||
@Inject
|
||||
private QuotaTariffUsageDao quotaTariffUsageDao;
|
||||
@Inject
|
||||
private EntityManager entityMgr;
|
||||
|
||||
private final Class<?>[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class, ResourceCounting.class};
|
||||
|
||||
|
|
@ -342,9 +355,9 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
|
||||
@Override
|
||||
public QuotaStatementResponse createQuotaStatementResponse(QuotaStatementCmd cmd) {
|
||||
Long accountId = getAccountIdForQuotaStatement(cmd);
|
||||
Long domainId = getDomainIdForQuotaStatement(cmd, accountId);
|
||||
List<QuotaUsageJoinVO> quotaUsages = _quotaService.getQuotaUsage(accountId, null, domainId, cmd.getUsageType(), cmd.getStartDate(), cmd.getEndDate());
|
||||
Long accountId = getAccountIdForQuotaStatement(cmd.getEntityOwnerId(), null);
|
||||
Pair<Long, List<Long>> baseDomainAndFilteredDomains = getDomainIdsForQuotaStatement(accountId, cmd.getDomainId(), cmd.isRecursive());
|
||||
List<QuotaUsageJoinVO> quotaUsages = _quotaService.getQuotaUsage(accountId, null, baseDomainAndFilteredDomains.second(), cmd.getUsageType(), cmd.getStartDate(), cmd.getEndDate());
|
||||
|
||||
logger.debug("Creating quota statement from [{}] usage records for parameters [{}].", quotaUsages.size(),
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, "accountName", "accountId", "projectId", "domainId", "startDate", "endDate", "usageType", "showResources"));
|
||||
|
|
@ -367,52 +380,16 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
Account account = _accountDao.findByIdIncludingRemoved(accountId);
|
||||
statement.setAccountId(account.getUuid());
|
||||
statement.setAccountName(account.getAccountName());
|
||||
domainId = account.getDomainId();
|
||||
}
|
||||
if (domainId != null) {
|
||||
DomainVO domain = domainDao.findByIdIncludingRemoved(domainId);
|
||||
Long baseDomainId = baseDomainAndFilteredDomains.first();
|
||||
if (baseDomainId != null) {
|
||||
DomainVO domain = domainDao.findByIdIncludingRemoved(baseDomainId);
|
||||
statement.setDomainId(domain.getUuid());
|
||||
}
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
protected Long getAccountIdForQuotaStatement(QuotaStatementCmd cmd) {
|
||||
if (Account.Type.NORMAL.equals(CallContext.current().getCallingAccount().getType())) {
|
||||
logger.debug("Limiting the Quota statement for the calling Account, as they are a User Account.");
|
||||
return CallContext.current().getCallingAccountId();
|
||||
}
|
||||
|
||||
long accountId = cmd.getEntityOwnerId();
|
||||
if (accountId != -1) {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
if (cmd.getDomainId() == null) {
|
||||
logger.debug("Limiting the Quota statement for the calling Account, as 'domainid' was not informed.");
|
||||
return CallContext.current().getCallingAccountId();
|
||||
}
|
||||
|
||||
logger.debug("Allowing admin/domain admin to generate the Quota statement for the provided Domain.");
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Long getDomainIdForQuotaStatement(QuotaStatementCmd cmd, Long accountId) {
|
||||
if (accountId != null) {
|
||||
logger.debug("Quota statement is already limited to Account [{}].", accountId);
|
||||
Account account = _accountDao.findByIdIncludingRemoved(accountId);
|
||||
return account.getDomainId();
|
||||
}
|
||||
|
||||
Long domainId = cmd.getDomainId();
|
||||
if (domainId != null) {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
logger.debug("Limiting the Quota statement for the calling Account's Domain.");
|
||||
return CallContext.current().getCallingAccount().getDomainId();
|
||||
}
|
||||
|
||||
protected void createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(List<QuotaUsageJoinVO> quotaUsages, Integer usageType) {
|
||||
if (usageType != null) {
|
||||
logger.debug("As the usage type [{}] was informed as parameter of the API quotaStatement, we will not create dummy records.", usageType);
|
||||
|
|
@ -1138,7 +1115,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
addAllPresetVariables(PresetVariables.class, quotaType, usageTypeVariablesAndDescriptions, null);
|
||||
List<String> usageTypeVariables = usageTypeVariablesAndDescriptions.stream().map(Pair::first).collect(Collectors.toList());
|
||||
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value())) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value(), QuotaConfig.QuotaActivationRuleTimeout.key())) {
|
||||
Map<String, String> newVariables = injectUsageTypeVariables(jsInterpreter, usageTypeVariables);
|
||||
String scriptToExecute = jsInterpreterHelper.replaceScriptVariables(activationRule, newVariables);
|
||||
jsInterpreter.executeScript(String.format("new Function(\"%s\")", scriptToExecute.replaceAll("\n", "")));
|
||||
|
|
@ -1158,6 +1135,184 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||
return createValidateActivationRuleResponse(activationRule, quotaName, false, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuotaResourceStatementResponse createQuotaResourceStatement(QuotaResourceStatementCmd cmd) {
|
||||
String resourceUuid = cmd.getId();
|
||||
Integer usageType = cmd.getUsageType();
|
||||
Date startDate = cmd.getStartDate();
|
||||
Date endDate = cmd.getEndDate();
|
||||
|
||||
if (startDate.after(endDate)) {
|
||||
throw new InvalidParameterValueException(String.format("The start date [%s] must be before the end date [%s].", startDate, endDate));
|
||||
}
|
||||
|
||||
InternalIdentity resource = retrieveResource(resourceUuid, usageType);
|
||||
if (resource == null) {
|
||||
logger.error("Could not find resource [{}] of type [{}]. Returning an empty list.", resourceUuid, usageType);
|
||||
return createQuotaResourceStatementResponse(resourceUuid, usageType, new ArrayList<>(), new BigDecimal(0));
|
||||
}
|
||||
|
||||
long id = resource.getId();
|
||||
|
||||
Long currentOwnerId = null;
|
||||
if (resource instanceof ControlledEntity) {
|
||||
currentOwnerId = ((ControlledEntity) resource).getAccountId();
|
||||
}
|
||||
Long accountId = getAccountIdForQuotaStatement(cmd.getEntityOwnerId(), currentOwnerId);
|
||||
Pair<Long, List<Long>> baseDomainAndFilteredDomains = getDomainIdsForQuotaStatement(accountId, cmd.getDomainId(), cmd.isRecursive());
|
||||
|
||||
Long resourceId = null;
|
||||
Long networkId = null;
|
||||
Long offeringId = null;
|
||||
|
||||
switch (usageType) {
|
||||
case UsageTypes.NETWORK_OFFERING:
|
||||
case UsageTypes.BACKUP:
|
||||
offeringId = id;
|
||||
break;
|
||||
case UsageTypes.NETWORK_BYTES_RECEIVED:
|
||||
case UsageTypes.NETWORK_BYTES_SENT:
|
||||
networkId = id;
|
||||
break;
|
||||
default:
|
||||
resourceId = id;
|
||||
}
|
||||
|
||||
logger.debug("Attempting to find quota usages with parameters usage type [{}], usage id [{}], network id [{}], offering id [{}], and between [{}] and [{}].",
|
||||
usageType, resourceId, networkId, offeringId, startDate, endDate);
|
||||
|
||||
List<QuotaUsageJoinVO> quotaUsageJoinList = quotaUsageJoinDao.findQuotaUsage(accountId, baseDomainAndFilteredDomains.second(), usageType, resourceId, networkId, offeringId, startDate, endDate, null);
|
||||
|
||||
logger.debug("Found [{}] quota usages using as parameter usage type [{}], usage id [{}], network id [{}], offering id [{}], and between [{}] and [{}].",
|
||||
quotaUsageJoinList.size(), usageType, resourceId, networkId, offeringId, startDate, endDate);
|
||||
|
||||
List<QuotaResourceStatementItemResponse> quotaResourceStatementItemResponseList = new ArrayList<>();
|
||||
BigDecimal totalQuotaUsed = new BigDecimal(0);
|
||||
List<QuotaTariffVO> quotaTariffs = _quotaTariffDao.listQuotaTariffs(null, null, usageType, null, null, true, null, null).first();
|
||||
|
||||
for (QuotaUsageJoinVO quotaUsageJoin : quotaUsageJoinList) {
|
||||
Account account = _accountMgr.getAccount(quotaUsageJoin.getAccountId());
|
||||
String accountUuid = account.getUuid();
|
||||
|
||||
List<QuotaTariffUsageVO> quotaTariffUsageList = quotaTariffUsageDao.listQuotaTariffUsages(quotaUsageJoin.getId());
|
||||
logger.debug("Found [{}] quota tariff usages associated to the quota usage [{}] of resource [{}] and type [{}] between [{}] and [{}].",
|
||||
quotaTariffUsageList.size(), quotaUsageJoin, resourceUuid, usageType, startDate, endDate);
|
||||
for (QuotaTariffUsageVO quotaTariffUsage: quotaTariffUsageList) {
|
||||
quotaResourceStatementItemResponseList.add(createQuotaResourceStatementItemResponse(quotaTariffUsage, quotaTariffs, quotaUsageJoin.getStartDate(),
|
||||
quotaUsageJoin.getEndDate(), accountUuid));
|
||||
totalQuotaUsed = totalQuotaUsed.add(quotaTariffUsage.getQuotaUsed());
|
||||
}
|
||||
}
|
||||
logger.debug("The total quota used of type [{}] between [{}] and [{}] for the resource [{}] was [{}].", usageType, startDate, endDate, resourceUuid, totalQuotaUsed);
|
||||
|
||||
return createQuotaResourceStatementResponse(resourceUuid, usageType, quotaResourceStatementItemResponseList, totalQuotaUsed);
|
||||
}
|
||||
|
||||
protected Long getAccountIdForQuotaStatement(long providedAccountId, Long fallbackAccountId) {
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
||||
if (providedAccountId != -1L) {
|
||||
Account account = _accountDao.findByIdIncludingRemoved(providedAccountId);
|
||||
_accountMgr.checkAccess(caller, null, false, account);
|
||||
logger.debug("Limiting the Quota resource statement for the provided Account [{}].", providedAccountId);
|
||||
return providedAccountId;
|
||||
}
|
||||
|
||||
Account.Type callerType = caller.getType();
|
||||
if (Account.Type.ADMIN.equals(callerType) || Account.Type.DOMAIN_ADMIN.equals(callerType)) {
|
||||
logger.debug("Not limiting the Quota resource statement for a specific Account, as no specific Account was provided and the caller is either an admin or domain admin.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (fallbackAccountId != null) {
|
||||
Account fallbackAccount = _accountDao.findByIdIncludingRemoved(fallbackAccountId);
|
||||
_accountMgr.checkAccess(caller, null, false, fallbackAccount);
|
||||
logger.debug("Limiting the Quota statement for the fallback Account [{}], as no specific Account was provided.", fallbackAccountId);
|
||||
return fallbackAccountId;
|
||||
}
|
||||
|
||||
logger.debug("Limiting the Quota resource statement for the calling account, as no specific Account was provided and no fallback account was provided.");
|
||||
return caller.getAccountId();
|
||||
}
|
||||
|
||||
protected Pair<Long, List<Long>> getDomainIdsForQuotaStatement(Long finalAccountId, Long providedDomainId, boolean isRecursive) {
|
||||
if (finalAccountId != null) {
|
||||
// Access to the provided account has already been validated
|
||||
logger.debug("Not limiting the Quota statement for a specific Domain, as we are already limiting by Account.");
|
||||
return new Pair<>(null, null);
|
||||
}
|
||||
|
||||
// User accounts will have already been limited to themselves
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
Long domainId = providedDomainId;
|
||||
|
||||
if (domainId != null) {
|
||||
Domain domain = domainDao.findByIdIncludingRemoved(domainId);
|
||||
_accountMgr.checkAccess(caller, domain);
|
||||
logger.debug("Limiting the Quota statement for the provided Domain [{}].", domainId);
|
||||
} else {
|
||||
domainId = caller.getDomainId();
|
||||
logger.debug("Limiting the Quota statement for the caller's Domain [{}], as no 'domainid' was provided.", domainId);
|
||||
}
|
||||
|
||||
if (isRecursive) {
|
||||
logger.debug("Allowing the Quota statement for the Domain's children, as the 'isrecursive' parameter was provided.");
|
||||
return new Pair<>(domainId, domainDao.getDomainAndChildrenIds(domainId));
|
||||
}
|
||||
|
||||
return new Pair<>(domainId, List.of(domainId));
|
||||
}
|
||||
|
||||
protected InternalIdentity retrieveResource(String resourceUuid, Integer usageType) {
|
||||
Class<?> clazz = QuotaTypes.getClazz(usageType);
|
||||
if (clazz == null) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid usage type [%s] provided.", usageType));
|
||||
}
|
||||
|
||||
logger.debug("Attempting to find a resource with ID [{}] and of type [{}].", resourceUuid, usageType);
|
||||
Object object = entityMgr.findByUuidIncludingRemoved(clazz, resourceUuid);
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
return (InternalIdentity) object;
|
||||
}
|
||||
|
||||
protected QuotaResourceStatementItemResponse createQuotaResourceStatementItemResponse(QuotaTariffUsageVO quotaTariffUsage, List<QuotaTariffVO> quotaTariffs,
|
||||
Date startDate, Date endDate, String accountUuid) {
|
||||
logger.trace("Creating quota resource statement item associated to quota tariff usage [{}].", quotaTariffUsage);
|
||||
QuotaResourceStatementItemResponse quotaResourceStatementItemResponse = new QuotaResourceStatementItemResponse();
|
||||
|
||||
QuotaTariffVO quotaTariff = quotaTariffs.stream().filter(quotaTariffVO -> quotaTariffUsage.getTariffId().equals(quotaTariffVO.getId())).findAny().orElse(null);
|
||||
|
||||
quotaResourceStatementItemResponse.setQuotaUsed(quotaTariffUsage.getQuotaUsed());
|
||||
quotaResourceStatementItemResponse.setStartDate(startDate);
|
||||
quotaResourceStatementItemResponse.setEndDate(endDate);
|
||||
quotaResourceStatementItemResponse.setAccountId(accountUuid);
|
||||
if (quotaTariff != null) {
|
||||
logger.trace("Quota usage details item will be associated to the quota tariff [{}].", quotaTariff);
|
||||
quotaResourceStatementItemResponse.setTariffId(quotaTariff.getUuid());
|
||||
quotaResourceStatementItemResponse.setTariffName(quotaTariff.getName());
|
||||
}
|
||||
|
||||
return quotaResourceStatementItemResponse;
|
||||
}
|
||||
|
||||
protected QuotaResourceStatementResponse createQuotaResourceStatementResponse(String resourceUuid, Integer usageType,
|
||||
List<QuotaResourceStatementItemResponse> quotaUsageDetailsItems, BigDecimal totalQuotaUsed) {
|
||||
logger.trace("Creating quota usage details list response associated to the resource of UUID [{}], with an usage type of [{}], [{}] quota" +
|
||||
" usage details items, and a total quota used of [{}].", resourceUuid, usageType, quotaUsageDetailsItems.size(), totalQuotaUsed);
|
||||
QuotaResourceStatementResponse quotaResourceStatementResponse = new QuotaResourceStatementResponse();
|
||||
|
||||
QuotaTypes quotaType = QuotaTypes.getQuotaType(usageType);
|
||||
quotaResourceStatementResponse.setUsageName(quotaType.getQuotaName());
|
||||
quotaResourceStatementResponse.setUnit(quotaType.getQuotaUnit());
|
||||
|
||||
quotaResourceStatementResponse.setQuotaUsageDetails(quotaUsageDetailsItems);
|
||||
quotaResourceStatementResponse.setTotalQuotaUsed(totalQuotaUsed);
|
||||
|
||||
return quotaResourceStatementResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether script variables are compatible with the usage type. First, we remove all script variables that correspond to the script's usage type variables.
|
||||
* Then, returns true if none of the remaining script variables match any usage types variables, and false otherwise.
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ public class QuotaStatementResponse extends BaseResponse {
|
|||
@Param(description = "ID of the Account.")
|
||||
private String accountId;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT)
|
||||
@SerializedName(ApiConstants.ACCOUNT_NAME)
|
||||
@Param(description = "Name of the Account.")
|
||||
private String accountName;
|
||||
|
||||
@SerializedName(ApiConstants.DOMAIN)
|
||||
@SerializedName(ApiConstants.DOMAIN_ID)
|
||||
@Param(description = "ID of the Domain.")
|
||||
private String domainId;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import com.cloud.utils.component.PluggableService;
|
|||
|
||||
public interface QuotaService extends PluggableService {
|
||||
|
||||
List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate);
|
||||
List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String accountName, List<Long> domainIds, Integer usageType, Date startDate, Date endDate);
|
||||
|
||||
List<QuotaBalanceVO> listQuotaBalancesForAccount(Long accountId, Date startDate, Date endDate);
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
|
|||
import org.apache.cloudstack.api.command.QuotaEnabledCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaListEmailConfigurationCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaPresetVariablesListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaResourceStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
|
||||
|
|
@ -131,6 +132,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
|
|||
cmdList.add(QuotaListEmailConfigurationCmd.class);
|
||||
cmdList.add(QuotaPresetVariablesListCmd.class);
|
||||
cmdList.add(QuotaValidateActivationRuleCmd.class);
|
||||
cmdList.add(QuotaResourceStatementCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
|
|
@ -203,15 +205,15 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
|
||||
public List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String accountName, List<Long> domainIds, Integer usageType, Date startDate, Date endDate) {
|
||||
if (startDate.after(endDate)) {
|
||||
throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
|
||||
}
|
||||
|
||||
logger.debug("Getting quota records of type [{}] for account [{}] in domain [{}], between [{}] and [{}].",
|
||||
usageType, accountId, domainId, startDate, endDate);
|
||||
logger.debug("Getting quota records of type [{}] for account [{}] in domains [{}], between [{}] and [{}].",
|
||||
usageType, accountId, domainIds, startDate, endDate);
|
||||
|
||||
return quotaUsageJoinDao.findQuotaUsage(accountId, domainId, usageType, null, null, null, startDate, endDate, null);
|
||||
return quotaUsageJoinDao.findQuotaUsage(accountId, domainIds, usageType, null, null, null, startDate, endDate, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -38,14 +38,15 @@ import com.cloud.exception.PermissionDeniedException;
|
|||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaCreditsListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
|
||||
import org.apache.cloudstack.api.command.QuotaValidateActivationRuleCmd;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
|
@ -156,7 +157,7 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
Date date = new Date();
|
||||
|
||||
@Mock
|
||||
Account accountMock;
|
||||
AccountVO accountMock;
|
||||
|
||||
@Mock
|
||||
DomainVO domainVoMock;
|
||||
|
|
@ -188,6 +189,9 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
@Mock
|
||||
User callerUserMock;
|
||||
|
||||
@Mock
|
||||
EntityManager entityManagerMock;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
CallContext.register(callerUserMock, callerAccountMock);
|
||||
|
|
@ -1024,107 +1028,161 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void getAccountIdForQuotaStatementTestLimitsToCallingAccountForNormalUser() {
|
||||
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
|
||||
public void getAccountIdForQuotaStatementTestReturnsProvidedAccount() {
|
||||
long providedAccountId = 200L;
|
||||
|
||||
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
|
||||
Mockito.doReturn(Account.Type.NORMAL).when(accountMock).getType();
|
||||
Mockito.when(accountDaoMock.findByIdIncludingRemoved(providedAccountId)).thenReturn(accountMock);
|
||||
Mockito.doNothing().when(accountManagerMock).checkAccess(callerAccountMock, null, false, accountMock);
|
||||
|
||||
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||
long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(providedAccountId, null);
|
||||
|
||||
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
|
||||
|
||||
Assert.assertEquals(Long.valueOf(callerAccountMock.getAccountId()), result);
|
||||
}
|
||||
Assert.assertEquals(200L, result);
|
||||
Mockito.verify(accountManagerMock).checkAccess(callerAccountMock, null, false, accountMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAccountIdForQuotaStatementTestReturnsEntityOwnerIdWhenProvided() {
|
||||
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
|
||||
public void getAccountIdForQuotaStatementTestReturnsNullWhenCallerIsAdminWithoutProvidedAccount() {
|
||||
Mockito.when(callerAccountMock.getType()).thenReturn(Account.Type.ADMIN);
|
||||
|
||||
Mockito.doReturn(42L).when(cmd).getEntityOwnerId();
|
||||
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(-1L, null);
|
||||
|
||||
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
|
||||
|
||||
Assert.assertEquals(Long.valueOf(42L), result);
|
||||
assertNull(result);
|
||||
Mockito.verify(accountManagerMock, Mockito.never()).getAccount(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAccountIdForQuotaStatementTestLimitsToCallingAccountWhenCallerIsAdminAndDomainIsNotProvided() {
|
||||
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
|
||||
public void getAccountIdForQuotaStatementTestReturnsNullWhenCallerIsDomainAdminWithoutProvidedAccount() {
|
||||
Mockito.when(callerAccountMock.getType()).thenReturn(Account.Type.DOMAIN_ADMIN);
|
||||
|
||||
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
|
||||
Mockito.doReturn(Account.Type.ADMIN).when(accountMock).getType();
|
||||
Mockito.doReturn(-1L).when(cmd).getEntityOwnerId();
|
||||
Mockito.doReturn(null).when(cmd).getDomainId();
|
||||
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(-1L, null);
|
||||
|
||||
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||
|
||||
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
|
||||
|
||||
Assert.assertEquals(Long.valueOf(callerAccountMock.getAccountId()), result);
|
||||
}
|
||||
assertNull(result);
|
||||
Mockito.verify(accountManagerMock, Mockito.never()).getAccount(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAccountIdForQuotaStatementTestReturnsNullWhenCallerIsAdminAndDomainIsProvided() {
|
||||
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
|
||||
public void getAccountIdForQuotaStatementTestReturnsFallbackAccountWhenNoAccountProvidedAndCallerIsNotAdmin() {
|
||||
Mockito.when(callerAccountMock.getType()).thenReturn(Account.Type.NORMAL);
|
||||
|
||||
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
|
||||
Mockito.doReturn(Account.Type.ADMIN).when(accountMock).getType();
|
||||
Mockito.doReturn(-1L).when(cmd).getEntityOwnerId();
|
||||
Mockito.doReturn(10L).when(cmd).getDomainId();
|
||||
Mockito.when(accountDaoMock.findByIdIncludingRemoved(300L)).thenReturn(accountMock);
|
||||
Mockito.doNothing().when(accountManagerMock).checkAccess(callerAccountMock, null, false, accountMock);
|
||||
|
||||
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||
long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(-1L, 300L);
|
||||
|
||||
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
|
||||
|
||||
Assert.assertNull(result);
|
||||
}
|
||||
assertEquals(300L, result);
|
||||
Mockito.verify(accountManagerMock).checkAccess(callerAccountMock, null, false, accountMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainIdForQuotaStatementTestReturnsAccountDomainIdWhenAccountIdIsProvided() {
|
||||
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
|
||||
AccountVO account = Mockito.mock(AccountVO.class);
|
||||
public void getAccountIdForQuotaStatementTestAccessDeniedForProvidedAccount() {
|
||||
Mockito.when(accountDaoMock.findByIdIncludingRemoved(200L)).thenReturn(accountMock);
|
||||
Mockito.doThrow(new PermissionDeniedException("Access denied"))
|
||||
.when(accountManagerMock).checkAccess(callerAccountMock, null, false, accountMock);
|
||||
|
||||
Mockito.doReturn(account).when(accountDaoMock).findByIdIncludingRemoved(55L);
|
||||
Mockito.doReturn(77L).when(account).getDomainId();
|
||||
|
||||
Long result = quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, 55L);
|
||||
|
||||
Assert.assertEquals(Long.valueOf(77L), result);
|
||||
Assert.assertThrows(PermissionDeniedException.class,
|
||||
() -> quotaResponseBuilderSpy.getAccountIdForQuotaStatement(200L, null));
|
||||
Mockito.verify(accountManagerMock).checkAccess(callerAccountMock, null, false, accountMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainIdForQuotaStatementTestReturnsProvidedDomainIdWhenAccountIdIsNull() {
|
||||
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
|
||||
public void getDomainIdsForQuotaStatementTestReturnsNullPairWhenAccountIsProvided() {
|
||||
Pair<Long, List<Long>> result = quotaResponseBuilderSpy.getDomainIdsForQuotaStatement(100L, null, false);
|
||||
|
||||
Mockito.doReturn(99L).when(cmd).getDomainId();
|
||||
|
||||
Long result = quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, null);
|
||||
|
||||
Assert.assertEquals(Long.valueOf(99L), result);
|
||||
assertNull(result.first());
|
||||
assertNull(result.second());
|
||||
Mockito.verify(domainDaoMock, Mockito.never()).findByIdIncludingRemoved(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainIdForQuotaStatementTestFallsBackToCallingAccountDomainIdWhenNeitherAccountNorDomainIsProvided() {
|
||||
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
|
||||
Account account = Mockito.mock(Account.class);
|
||||
public void getDomainIdsForQuotaStatementTestReturnsProvidedDomainIdNonRecursively() {
|
||||
Mockito.when(domainDaoMock.findByIdIncludingRemoved(5L)).thenReturn(domainVoMock);
|
||||
Mockito.doNothing().when(accountManagerMock).checkAccess(callerAccountMock, domainVoMock);
|
||||
|
||||
Mockito.doReturn(null).when(cmd).getDomainId();
|
||||
Mockito.doReturn(123L).when(account).getDomainId();
|
||||
Mockito.doReturn(account).when(callContextMock).getCallingAccount();
|
||||
Pair<Long, List<Long>> result = quotaResponseBuilderSpy.getDomainIdsForQuotaStatement(null, 5L, false);
|
||||
|
||||
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||
assertEquals(5L, (long) result.first());
|
||||
assertEquals(List.of(5L), result.second());
|
||||
Mockito.verify(accountManagerMock).checkAccess(callerAccountMock, domainVoMock);
|
||||
}
|
||||
|
||||
Long result = quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, null);
|
||||
@Test
|
||||
public void getDomainIdsForQuotaStatementTestReturnsCallerDomainNonRecursively() {
|
||||
Mockito.when(callerAccountMock.getDomainId()).thenReturn(7L);
|
||||
|
||||
Assert.assertEquals(123L, result.longValue());
|
||||
}
|
||||
Pair<Long, List<Long>> result = quotaResponseBuilderSpy.getDomainIdsForQuotaStatement(null, null, false);
|
||||
|
||||
assertEquals(7L, (long) result.first());
|
||||
assertEquals(List.of(7L), result.second());
|
||||
Mockito.verify(domainDaoMock, Mockito.never()).findByIdIncludingRemoved(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainIdsForQuotaStatementTestReturnsProvidedDomainRecursively() {
|
||||
List<Long> domainAndChildren = List.of(5L, 10L, 15L);
|
||||
Mockito.when(domainDaoMock.findByIdIncludingRemoved(5L)).thenReturn(domainVoMock);
|
||||
Mockito.when(domainDaoMock.getDomainAndChildrenIds(5L)).thenReturn(domainAndChildren);
|
||||
Mockito.doNothing().when(accountManagerMock).checkAccess(callerAccountMock, domainVoMock);
|
||||
|
||||
Pair<Long, List<Long>> result = quotaResponseBuilderSpy.getDomainIdsForQuotaStatement(null, 5L, true);
|
||||
|
||||
assertEquals(5L, (long) result.first());
|
||||
assertEquals(domainAndChildren, result.second());
|
||||
Mockito.verify(domainDaoMock).getDomainAndChildrenIds(5L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainIdsForQuotaStatementReturnsCallerDomainRecursively() {
|
||||
List<Long> domainAndChildren = List.of(1L, 2L, 3L);
|
||||
Mockito.when(callerAccountMock.getDomainId()).thenReturn(1L);
|
||||
|
||||
Mockito.when(domainDaoMock.getDomainAndChildrenIds(1L)).thenReturn(domainAndChildren);
|
||||
|
||||
Pair<Long, List<Long>> result = quotaResponseBuilderSpy.getDomainIdsForQuotaStatement(null, null, true);
|
||||
|
||||
assertEquals(1L, (long) result.first());
|
||||
assertEquals(domainAndChildren, result.second());
|
||||
Mockito.verify(domainDaoMock).getDomainAndChildrenIds(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDomainIdsForQuotaStatementTestThrowsAccessDeniedForProvidedDomain() {
|
||||
Mockito.when(domainDaoMock.findByIdIncludingRemoved(5L)).thenReturn(domainVoMock);
|
||||
Mockito.doThrow(new PermissionDeniedException("Access denied"))
|
||||
.when(accountManagerMock).checkAccess(callerAccountMock, domainVoMock);
|
||||
|
||||
Assert.assertThrows(PermissionDeniedException.class,
|
||||
() -> quotaResponseBuilderSpy.getDomainIdsForQuotaStatement(null, 5L, false));
|
||||
Mockito.verify(accountManagerMock).checkAccess(callerAccountMock, domainVoMock);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void retrieveResourceTestThrowsExceptionForInvalidUsageType() {
|
||||
Integer invalidUsageType = 999;
|
||||
quotaResponseBuilderSpy.retrieveResource("validUuid", invalidUsageType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveResourceTestReturnsNullForNonexistentResource() {
|
||||
String invalidUuid = "nonexistentUuid";
|
||||
Integer validUsageType = QuotaTypes.ALLOCATED_VM;
|
||||
|
||||
Mockito.doReturn(null).when(entityManagerMock).findByUuidIncludingRemoved(Mockito.any(), Mockito.eq(invalidUuid));
|
||||
InternalIdentity result = quotaResponseBuilderSpy.retrieveResource(invalidUuid, validUsageType);
|
||||
|
||||
Assert.assertNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveResourceTestReturnsCorrectResource() {
|
||||
String validUuid = "validUuid";
|
||||
Integer validUsageType = QuotaTypes.ALLOCATED_VM;
|
||||
InternalIdentity mockResource = Mockito.mock(InternalIdentity.class);
|
||||
|
||||
Mockito.doReturn(mockResource).when(entityManagerMock).findByUuidIncludingRemoved(Mockito.any(), Mockito.eq(validUuid));
|
||||
|
||||
InternalIdentity result = quotaResponseBuilderSpy.retrieveResource(validUuid, validUsageType);
|
||||
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(mockResource, result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,12 +117,12 @@ public class QuotaServiceImplTest extends TestCase {
|
|||
public void testGetQuotaUsage() {
|
||||
final long accountId = 2L;
|
||||
final String accountName = "admin123";
|
||||
final long domainId = 1L;
|
||||
final List<Long> domainIds = List.of(1L);
|
||||
final Date startDate = new DateTime().minusDays(2).toDate();
|
||||
final Date endDate = new Date();
|
||||
|
||||
quotaServiceImplSpy.getQuotaUsage(accountId, accountName, domainId, QuotaTypes.IP_ADDRESS, startDate, endDate);
|
||||
Mockito.verify(quotaUsageJoinDaoMock, Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(),
|
||||
quotaServiceImplSpy.getQuotaUsage(accountId, accountName, domainIds, QuotaTypes.IP_ADDRESS, startDate, endDate);
|
||||
Mockito.verify(quotaUsageJoinDaoMock, Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainIds), Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(Date.class), Mockito.any(Date.class), Mockito.any());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -137,18 +137,19 @@ if [ -f "$SYSTEM_FILE" ]; then
|
|||
chmod 644 /usr/local/share/ca-certificates/cloudstack/ca.crt
|
||||
update-ca-certificates > /dev/null 2>&1 || true
|
||||
|
||||
# Import CA cert(s) into realhostip.keystore so the SSVM JVM
|
||||
# (which overrides the truststore via -Djavax.net.ssl.trustStore in _run.sh)
|
||||
# can trust servers signed by the CloudStack CA
|
||||
REALHOSTIP_KS_FILE="$(dirname "$(dirname "$PROPS_FILE")")/certs/realhostip.keystore"
|
||||
REALHOSTIP_PASS="vmops.com"
|
||||
if [ -f "$REALHOSTIP_KS_FILE" ]; then
|
||||
# Also import CA cert(s) into systemvm.keystore. KS_FILE (cloud.jks) above
|
||||
# is the agent's mTLS keystore; the SSVM JVM, however, reads its truststore
|
||||
# from systemvm.keystore (see -Djavax.net.ssl.trustStore in _run.sh), so the
|
||||
# CA must be added here too for the SSVM to trust CloudStack-CA-signed servers.
|
||||
SYSTEMVM_KS_FILE="$(dirname "$(dirname "$PROPS_FILE")")/certs/systemvm.keystore"
|
||||
SYSTEMVM_PASS="vmops.com"
|
||||
if [ -f "$SYSTEMVM_KS_FILE" ]; then
|
||||
awk 'BEGIN{n=0} /-----BEGIN CERTIFICATE-----/{n++} n>0{print > "cloudca." n }' "$CACERT_FILE"
|
||||
for caChain in $(ls cloudca.* 2>/dev/null); do
|
||||
keytool -delete -noprompt -alias "$caChain" -keystore "$REALHOSTIP_KS_FILE" \
|
||||
-storepass "$REALHOSTIP_PASS" > /dev/null 2>&1 || true
|
||||
keytool -delete -noprompt -alias "$caChain" -keystore "$SYSTEMVM_KS_FILE" \
|
||||
-storepass "$SYSTEMVM_PASS" > /dev/null 2>&1 || true
|
||||
keytool -import -noprompt -trustcacerts -alias "$caChain" -file "$caChain" \
|
||||
-keystore "$REALHOSTIP_KS_FILE" -storepass "$REALHOSTIP_PASS" > /dev/null 2>&1
|
||||
-keystore "$SYSTEMVM_KS_FILE" -storepass "$SYSTEMVM_PASS" > /dev/null 2>&1
|
||||
done
|
||||
rm -f cloudca.*
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@
|
|||
package com.cloud.agent.manager.allocator.impl;
|
||||
|
||||
import com.cloud.agent.manager.allocator.HostAllocator;
|
||||
import com.cloud.api.ApiDBUtils;
|
||||
import com.cloud.capacity.CapacityManager;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.AdapterBase;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
@ -69,6 +71,29 @@ public abstract class BaseAllocator extends AdapterBase implements HostAllocator
|
|||
clusterHosts.addAll(hostsWithTagRules);
|
||||
}
|
||||
|
||||
protected void filterHostsBasedOnGuestOsRules(VMTemplateVO vmTemplate, List<? extends Host> clusterHosts) {
|
||||
if (clusterHosts.isEmpty()) {
|
||||
logger.info("Will not filter hosts based on guest OS as there is no available hosts left to verify.");
|
||||
return;
|
||||
}
|
||||
|
||||
String templateGuestOSName = ApiDBUtils.getTemplateGuestOSName(vmTemplate);
|
||||
List<HostVO> incompatibleHosts = hostDao.findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(templateGuestOSName);
|
||||
|
||||
if (incompatibleHosts.isEmpty()) {
|
||||
logger.info("No incompatible hosts found with guest OS rules matching the VM guest OS [{}].", templateGuestOSName);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Found incompatible hosts {} with guest OS rules that did not match the VM guest OS [{}]. They will be removed from the suitable hosts list.",
|
||||
incompatibleHosts, templateGuestOSName);
|
||||
clusterHosts.removeAll(incompatibleHosts);
|
||||
|
||||
if (clusterHosts.isEmpty()) {
|
||||
logger.info("After filtering by guest OS rules, no compatible hosts were found for VM with OS [{}].", templateGuestOSName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds hosts with enough CPU capability and enough CPU capacity to the suitable hosts list.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ public class FirstFitAllocator extends BaseAllocator {
|
|||
String hostTagOnTemplate = template.getTemplateTag();
|
||||
String paramAsStringToLog = String.format("zone [%s], pod [%s], cluster [%s]", dcId, podId, clusterId);
|
||||
|
||||
List<HostVO> suitableHosts = retrieveHosts(vmProfile, type, (List<HostVO>) hosts, clusterId, podId, dcId, hostTagOnOffering, hostTagOnTemplate);
|
||||
List<HostVO> suitableHosts = retrieveHosts(vmProfile, type, (List<HostVO>) hosts, template, clusterId, podId, dcId, hostTagOnOffering, hostTagOnTemplate);
|
||||
|
||||
if (suitableHosts.isEmpty()) {
|
||||
logger.info("No suitable host found for VM [{}] in {}.", vmProfile, paramAsStringToLog);
|
||||
|
|
@ -137,8 +137,8 @@ public class FirstFitAllocator extends BaseAllocator {
|
|||
return allocateTo(vmProfile, plan, offering, template, avoid, suitableHosts, returnUpTo, considerReservedCapacity, account);
|
||||
}
|
||||
|
||||
protected List<HostVO> retrieveHosts(VirtualMachineProfile vmProfile, Type type, List<HostVO> hostsToFilter, Long clusterId, Long podId, long dcId, String hostTagOnOffering,
|
||||
String hostTagOnTemplate) {
|
||||
protected List<HostVO> retrieveHosts(VirtualMachineProfile vmProfile, Type type, List<HostVO> hostsToFilter, VMTemplateVO template,
|
||||
Long clusterId, Long podId, long dcId, String hostTagOnOffering, String hostTagOnTemplate) {
|
||||
String haVmTag = (String) vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
|
||||
List<HostVO> clusterHosts;
|
||||
|
||||
|
|
@ -159,6 +159,7 @@ public class FirstFitAllocator extends BaseAllocator {
|
|||
filterHostsWithUefiEnabled(type, vmProfile, clusterId, podId, dcId, clusterHosts);
|
||||
|
||||
addHostsBasedOnTagRules(hostTagOnOffering, clusterHosts);
|
||||
filterHostsBasedOnGuestOsRules(template, clusterHosts);
|
||||
|
||||
return clusterHosts;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class RandomAllocator extends BaseAllocator {
|
|||
private ResourceManager _resourceMgr;
|
||||
|
||||
protected List<Host> findSuitableHosts(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
|
||||
boolean considerReservedCapacity) {
|
||||
boolean considerReservedCapacity) {
|
||||
if (type == Host.Type.Storage) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -115,6 +115,7 @@ public class RandomAllocator extends BaseAllocator {
|
|||
}
|
||||
|
||||
addHostsBasedOnTagRules(offeringHostTag, availableHosts);
|
||||
filterHostsBasedOnGuestOsRules(template, availableHosts);
|
||||
|
||||
return availableHosts;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import javax.annotation.PostConstruct;
|
|||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.storage.GuestOSVO;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
|
|
@ -2369,4 +2370,15 @@ public class ApiDBUtils {
|
|||
public static List<CPU.CPUArch> listZoneClustersArchs(long zoneId) {
|
||||
return s_clusterDao.getClustersArchsByZone(zoneId);
|
||||
}
|
||||
|
||||
public static String getTemplateGuestOSName(VMTemplateVO template) {
|
||||
long guestOSId = template.getGuestOSId();
|
||||
GuestOSVO guestOS = s_guestOSDao.findById(guestOSId);
|
||||
|
||||
if (guestOS == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return guestOS.getDisplayName();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
|
|||
hostResponse.setHaHost(containsHostHATag(hostTags));
|
||||
hostResponse.setExplicitHostTags(host.getExplicitTag());
|
||||
hostResponse.setImplicitHostTags(host.getImplicitTag());
|
||||
hostResponse.setGuestOsRule(host.getGuestOsRule());
|
||||
|
||||
hostResponse.setHypervisorVersion(host.getHypervisorVersion());
|
||||
if (host.getArch() != null) {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
|||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -401,7 +401,8 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
|
|||
String injectableTag = injectableTagsBuilder.toString();
|
||||
|
||||
for (StoragePoolJoinVO storagePoolJoinVO : storagePools) {
|
||||
if (TagAsRuleHelper.interpretTagAsRule(storagePoolJoinVO.getTag(), injectableTag, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value())) {
|
||||
if (GenericRuleHelper.interpretTagAsRule(storagePoolJoinVO.getTag(), injectableTag, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value(),
|
||||
VolumeApiServiceImpl.storageTagRuleExecutionTimeout.key())) {
|
||||
StoragePoolVO storagePoolVO = storagePoolDao.findById(storagePoolJoinVO.getId());
|
||||
if (storagePoolVO != null) {
|
||||
filteredPools.add(storagePoolVO);
|
||||
|
|
|
|||
|
|
@ -173,6 +173,9 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
|
|||
@Column(name = "guest_os_category_name")
|
||||
private String osCategoryName;
|
||||
|
||||
@Column(name = "guest_os_rule")
|
||||
private String guestOsRule;
|
||||
|
||||
@Column(name = "tag")
|
||||
private String tag;
|
||||
|
||||
|
|
@ -373,6 +376,10 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
|
|||
return osCategoryName;
|
||||
}
|
||||
|
||||
public String getGuestOsRule() {
|
||||
return guestOsRule;
|
||||
}
|
||||
|
||||
public Long getJobId() {
|
||||
return jobId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
|||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.userdata.UserDataManager;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.cloudstack.vm.UnmanagedVMsManager;
|
||||
import org.apache.cloudstack.vm.lease.VMLeaseManager;
|
||||
|
|
@ -5115,7 +5115,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
|
||||
if ((CollectionUtils.isNotEmpty(tagsAsString) && tagsAsString.containsAll(listOfTags)) ||
|
||||
(tagsOnPool.size() == 1 && tagsOnPool.get(0).isTagARule() &&
|
||||
TagAsRuleHelper.interpretTagAsRule(tagsOnPool.get(0).getTag(), tags, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value()))) {
|
||||
GenericRuleHelper.interpretTagAsRule(tagsOnPool.get(0).getTag(), tags, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value(),
|
||||
VolumeApiServiceImpl.storageTagRuleExecutionTimeout.key()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -5158,7 +5159,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||
|
||||
if ((CollectionUtils.isNotEmpty(tagsAsString) && tagsAsString.containsAll(listOfHostTags)) ||
|
||||
(tagsOnHost.size() == 1 && tagsOnHost.get(0).getIsTagARule() &&
|
||||
TagAsRuleHelper.interpretTagAsRule(tagsOnHost.get(0).getTag(), hostTags, HostTagsDao.hostTagRuleExecutionTimeout.value()))) {
|
||||
GenericRuleHelper.interpretTagAsRule(tagsOnHost.get(0).getTag(), hostTags, HostTagsDao.hostTagRuleExecutionTimeout.value(),
|
||||
HostTagsDao.hostTagRuleExecutionTimeout.key()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import java.util.stream.Collectors;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.api.ApiDBUtils;
|
||||
import com.cloud.gpu.dao.VgpuProfileDao;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
|
||||
|
|
@ -805,18 +806,49 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DetailVO guestOsRule = _hostDetailsDao.findDetail(host.getId(), HostVO.GUEST_OS_RULE);
|
||||
if (guestOsRule != null) {
|
||||
return isHostGuestOsCompatible(vmProfile, host, guestOsRule);
|
||||
}
|
||||
|
||||
DetailVO guestOsCategoryId = _hostDetailsDao.findDetail(host.getId(), HostVO.GUEST_OS_CATEGORY_ID);
|
||||
if (guestOsCategoryId != null) {
|
||||
return isHostGuestOsCategoryCompatible(vmProfile, host, guestOsCategoryId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isHostGuestOsCategoryCompatible(VirtualMachineProfile vmProfile, HostVO host, DetailVO guestOsCategoryId) {
|
||||
long guestOSId = vmProfile.getTemplate().getGuestOSId();
|
||||
GuestOSVO guestOS = _guestOSDao.findById(guestOSId);
|
||||
if (guestOS != null) {
|
||||
long guestOSCategoryId = guestOS.getCategoryId();
|
||||
DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), "guest.os.category.id");
|
||||
if (hostDetail != null) {
|
||||
String guestOSCategoryIdString = hostDetail.getValue();
|
||||
if (String.valueOf(guestOSCategoryId) != guestOSCategoryIdString) {
|
||||
logger.debug("The last host has different guest.os.category.id than guest os category of VM, skipping");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (guestOS == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
long guestOSCategoryId = guestOS.getCategoryId();
|
||||
String guestOSCategoryIdString = guestOsCategoryId.getValue();
|
||||
if (!String.valueOf(guestOSCategoryId).equals(guestOSCategoryIdString)) {
|
||||
logger.debug("The last host has different `{}` than guest os category of VM, skipping", Host.GUEST_OS_CATEGORY_ID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isHostGuestOsCompatible(VirtualMachineProfile vmProfile, HostVO host, DetailVO guestOsRule) {
|
||||
VMTemplateVO template = (VMTemplateVO) vmProfile.getTemplate();
|
||||
String templateGuestOSName = ApiDBUtils.getTemplateGuestOSName(template);
|
||||
|
||||
List<HostVO> incompatibleHosts = _hostDao.findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(templateGuestOSName);
|
||||
|
||||
boolean isHostCompatible = incompatibleHosts.stream().noneMatch(incompatibleHost -> incompatibleHost.getId() == host.getId());
|
||||
|
||||
if (!isHostCompatible) {
|
||||
logger.debug("The template [{}] is incompatible with the host [{}] due to the guest OS rule [{}].", template, host, guestOsRule);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
|||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -2046,31 +2046,74 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||
_hostDao.update(host.getId(), host);
|
||||
}
|
||||
|
||||
private void updateHostGuestOSCategory(Long hostId, Long guestOSCategoryId) {
|
||||
// Verify that the guest OS Category exists
|
||||
if (!(guestOSCategoryId > 0) || _guestOSCategoryDao.findById(guestOSCategoryId) == null) {
|
||||
/**
|
||||
* Updates the guest OS details related to the host ({@code guest.os.rule} and {@code guest.os.category.id}); only one can be set at a time for a given host.
|
||||
*/
|
||||
private void updateGuestOsRelatedFields(Long hostId, Long cmdOsCategoryId, String cmdGuestOsRule) {
|
||||
if (ObjectUtils.allNotNull(cmdOsCategoryId, cmdGuestOsRule)) {
|
||||
throw new InvalidParameterValueException("Informing both guest OS category and guest OS rule is invalid; please, inform only one of them.");
|
||||
}
|
||||
|
||||
if (cmdOsCategoryId != null) {
|
||||
updateHostCategoryOs(hostId, cmdOsCategoryId);
|
||||
} else if (cmdGuestOsRule != null) {
|
||||
updateHostGuestOsRule(hostId, cmdGuestOsRule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates/removes a guest OS rule to a host and removes any guest OS category associated with it.
|
||||
*/
|
||||
private void updateHostGuestOsRule(Long hostId, String cmdGuestOsRule) {
|
||||
final DetailVO hostGuestOsRule = _hostDetailsDao.findDetail(hostId, Host.GUEST_OS_RULE);
|
||||
|
||||
if (StringUtils.isNotBlank(cmdGuestOsRule)) {
|
||||
createOrUpdateHostDetails(hostGuestOsRule, cmdGuestOsRule, Host.GUEST_OS_RULE, hostId);
|
||||
} else if (hostGuestOsRule != null) {
|
||||
_hostDetailsDao.remove(hostGuestOsRule.getId());
|
||||
}
|
||||
removeHostDetails(hostId, Host.GUEST_OS_CATEGORY_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a host details if it exists.
|
||||
*/
|
||||
private void removeHostDetails(Long hostId, String detailString) {
|
||||
DetailVO detailVO = _hostDetailsDao.findDetail(hostId, detailString);
|
||||
|
||||
if (detailVO != null) {
|
||||
_hostDetailsDao.remove(detailVO.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void createOrUpdateHostDetails(DetailVO hostDetail, String detailValue, String detailName, Long hostId) {
|
||||
if (hostDetail != null) {
|
||||
hostDetail.setValue(detailValue);
|
||||
_hostDetailsDao.update(hostDetail.getId(), hostDetail);
|
||||
} else {
|
||||
Map<String, String> detail = new HashMap<>();
|
||||
detail.put(detailName, detailValue);
|
||||
_hostDetailsDao.persist(hostId, detail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates/removes a valid guest OS category to a host and removes any guest OS rules associated with it.
|
||||
*/
|
||||
private void updateHostCategoryOs(Long hostId, Long cmdOsCategoryId) {
|
||||
if (cmdOsCategoryId <= 0 || _guestOSCategoryDao.findById(cmdOsCategoryId) == null) {
|
||||
throw new InvalidParameterValueException("Please specify a valid guest OS category.");
|
||||
}
|
||||
|
||||
final GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId);
|
||||
final DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id");
|
||||
GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(cmdOsCategoryId);
|
||||
DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, Host.GUEST_OS_CATEGORY_ID);
|
||||
|
||||
if (guestOSCategory != null && !GuestOSCategoryVO.CATEGORY_NONE.equalsIgnoreCase(guestOSCategory.getName())) {
|
||||
// Create/Update an entry for guest.os.category.id
|
||||
if (guestOSDetail != null) {
|
||||
guestOSDetail.setValue(String.valueOf(guestOSCategory.getId()));
|
||||
_hostDetailsDao.update(guestOSDetail.getId(), guestOSDetail);
|
||||
} else {
|
||||
final Map<String, String> detail = new HashMap<>();
|
||||
detail.put("guest.os.category.id", String.valueOf(guestOSCategory.getId()));
|
||||
_hostDetailsDao.persist(hostId, detail);
|
||||
}
|
||||
} else {
|
||||
// Delete any existing entry for guest.os.category.id
|
||||
if (guestOSDetail != null) {
|
||||
_hostDetailsDao.remove(guestOSDetail.getId());
|
||||
}
|
||||
createOrUpdateHostDetails(guestOSDetail, String.valueOf(guestOSCategory.getId()), Host.GUEST_OS_CATEGORY_ID, hostId);
|
||||
} else if (guestOSDetail != null) {
|
||||
_hostDetailsDao.remove(guestOSDetail.getId());
|
||||
}
|
||||
removeHostDetails(hostId, Host.GUEST_OS_RULE);
|
||||
}
|
||||
|
||||
private void removeStorageAccessGroupsOnPodsInZone(long zoneId, List<String> newStoragePoolTags, List<String> tagsToDeleteOnZone) {
|
||||
|
|
@ -2821,16 +2864,17 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||
|
||||
@Override
|
||||
public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException {
|
||||
return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(),
|
||||
return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(), cmd.getGuestOsRule(),
|
||||
cmd.getAllocationState(), cmd.getUrl(), cmd.getHostTags(), cmd.getIsTagARule(), cmd.getAnnotation(), false,
|
||||
cmd.getExternalDetails(), cmd.isCleanupExternalDetails());
|
||||
}
|
||||
|
||||
private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String allocationState,
|
||||
private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String guestOsRule, String allocationState,
|
||||
String url, List<String> hostTags, Boolean isTagARule, String annotation,
|
||||
boolean isUpdateFromHostHealthCheck, Map<String, String> externalDetails,
|
||||
boolean cleanupExternalDetails) throws NoTransitionException {
|
||||
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(isTagARule));
|
||||
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.GUEST_OS_RULE, guestOsRule != null);
|
||||
|
||||
// Verify that the host exists
|
||||
final HostVO host = _hostDao.findById(hostId);
|
||||
|
|
@ -2847,9 +2891,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||
updateHostName(host, name);
|
||||
}
|
||||
|
||||
if (guestOSCategoryId != null) {
|
||||
updateHostGuestOSCategory(hostId, guestOSCategoryId);
|
||||
}
|
||||
updateGuestOsRelatedFields(hostId, guestOSCategoryId, guestOsRule);
|
||||
|
||||
if (hostTags != null) {
|
||||
updateHostTags(host, hostId, hostTags, isTagARule);
|
||||
|
|
@ -2919,7 +2961,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||
|
||||
@Override
|
||||
public Host autoUpdateHostAllocationState(Long hostId, ResourceState.Event resourceEvent) throws NoTransitionException {
|
||||
return updateHost(hostId, null, null, resourceEvent.toString(), null, null, null, null, true, null, false);
|
||||
return updateHost(hostId, null, null, null, resourceEvent.toString(), null, null, null, null, true, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -3639,7 +3681,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||
// If the server's private IP is the same as is public IP, this host has
|
||||
// a host-only private network. Don't check for conflicts with the
|
||||
// private IP address table.
|
||||
if (!ObjectUtils.equals(serverPrivateIP, serverPublicIP)) {
|
||||
if (!StringUtils.equals(serverPrivateIP, serverPublicIP)) {
|
||||
if (!_privateIPAddressDao.mark(dc.getId(), pod.getId(), serverPrivateIP)) {
|
||||
// If the server's private IP address is already in the
|
||||
// database, return false
|
||||
|
|
@ -4284,7 +4326,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||
return null;
|
||||
} else {
|
||||
_hostDao.loadDetails(host);
|
||||
final DetailVO detail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id");
|
||||
final DetailVO detail = _hostDetailsDao.findDetail(hostId, Host.GUEST_OS_CATEGORY_ID);
|
||||
if (detail == null) {
|
||||
return null;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -222,9 +222,6 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
|
|||
_configDao.update(Config.SecStorageEncryptCopy.key(), Config.SecStorageEncryptCopy.getCategory(), "false");
|
||||
logger.debug("ConfigurationServer made secondary storage copy encrypt set to false.");
|
||||
|
||||
_configDao.update("secstorage.secure.copy.cert", "realhostip");
|
||||
logger.debug("ConfigurationServer made secondary storage copy use realhostip.");
|
||||
|
||||
_configDao.update("user.password.encoders.exclude", "MD5,LDAP,PLAINTEXT");
|
||||
logger.debug("Configuration server excluded insecure encoders");
|
||||
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
|
|||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
|
||||
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
@ -3904,7 +3904,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
boolean result;
|
||||
if (storagePoolTags.second()) {
|
||||
result = TagAsRuleHelper.interpretTagAsRule(storageTagsList.get(0), diskOfferingTags, storageTagRuleExecutionTimeout.value());
|
||||
result = GenericRuleHelper.interpretTagAsRule(storageTagsList.get(0), diskOfferingTags, storageTagRuleExecutionTimeout.value(),
|
||||
storageTagRuleExecutionTimeout.key());
|
||||
} else {
|
||||
result = CollectionUtils.isSubCollection(Arrays.asList(newDiskOfferingTagsAsStringArray), storageTagsList);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,11 +87,6 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor
|
|||
final Map<String, String> configs = _configDao.getConfiguration("management-server", params);
|
||||
_proxy = configs.get(Config.SecStorageProxy.key());
|
||||
|
||||
String cert = configs.get("secstorage.ssl.cert.domain");
|
||||
if (!"realhostip.com".equalsIgnoreCase(cert)) {
|
||||
logger.warn("Only realhostip.com ssl cert is supported, ignoring self-signed and other certs");
|
||||
}
|
||||
|
||||
_copyAuthPasswd = configs.get("secstorage.copy.password");
|
||||
|
||||
DownloadListener dl = new DownloadListener(this);
|
||||
|
|
|
|||
|
|
@ -231,8 +231,8 @@ public class UploadMonitorImpl extends ManagerBase implements UploadMonitor {
|
|||
UploadVO upload = extractURLList.get(0);
|
||||
String uploadUrl = extractURLList.get(0).getUploadUrl();
|
||||
String[] token = uploadUrl.split("/");
|
||||
// example: uploadUrl = https://10-11-101-112.realhostip.com/userdata/2fdd9a70-9c4a-4a04-b1d5-1e41c221a1f9.iso
|
||||
// then token[2] = 10-11-101-112.realhostip.com, token[4] = 2fdd9a70-9c4a-4a04-b1d5-1e41c221a1f9.iso
|
||||
// example: uploadUrl = https://10-11-101-112.example.com/userdata/2fdd9a70-9c4a-4a04-b1d5-1e41c221a1f9.iso
|
||||
// then token[2] = 10-11-101-112.example.com, token[4] = 2fdd9a70-9c4a-4a04-b1d5-1e41c221a1f9.iso
|
||||
String hostname = ep.getPublicAddr().replace(".", "-") + ".";
|
||||
if ((token != null) && (token.length == 5) && (token[2].equals(hostname + _ssvmUrlDomain))) // ssvm publicip and domain suffix not changed
|
||||
return extractURLList.get(0);
|
||||
|
|
@ -365,7 +365,9 @@ public class UploadMonitorImpl extends ManagerBase implements UploadMonitor {
|
|||
if (_ssvmUrlDomain != null && _ssvmUrlDomain.length() > 0) {
|
||||
hostname = hostname + "." + _ssvmUrlDomain;
|
||||
} else {
|
||||
hostname = hostname + ".realhostip.com";
|
||||
logger.warn("SSL copy is enabled but secstorage.ssl.cert.domain is not configured; "
|
||||
+ "using IP address directly. Configure a wildcard SSL certificate domain for proper HTTPS support.");
|
||||
hostname = ipAddress;
|
||||
}
|
||||
}
|
||||
return scheme + "://" + hostname + "/userdata/" + uuid;
|
||||
|
|
@ -376,11 +378,6 @@ public class UploadMonitorImpl extends ManagerBase implements UploadMonitor {
|
|||
final Map<String, String> configs = _configDao.getConfiguration("management-server", params);
|
||||
_sslCopy = Boolean.parseBoolean(configs.get("secstorage.encrypt.copy"));
|
||||
|
||||
String cert = configs.get("secstorage.secure.copy.cert");
|
||||
if ("realhostip.com".equalsIgnoreCase(cert)) {
|
||||
logger.warn("Only realhostip.com ssl cert is supported, ignoring self-signed and other certs");
|
||||
}
|
||||
|
||||
_ssvmUrlDomain = configs.get("secstorage.ssl.cert.domain");
|
||||
|
||||
_agentMgr.registerForHostEvents(new UploadListener(this), true, false, false);
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ public class HeuristicRuleHelper {
|
|||
* @return the {@link DataStore} returned by the script.
|
||||
*/
|
||||
public DataStore interpretHeuristicRule(String rule, HeuristicType heuristicType, Object obj, long zoneId) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(HEURISTICS_SCRIPT_TIMEOUT)) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(HEURISTICS_SCRIPT_TIMEOUT, StorageManager.HEURISTICS_SCRIPT_TIMEOUT.key())) {
|
||||
buildPresetVariables(jsInterpreter, heuristicType, zoneId, obj);
|
||||
Object scriptReturn = jsInterpreter.executeScript(rule);
|
||||
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ public class FirstFitAllocatorTest {
|
|||
Mockito.doReturn(account).when(virtualMachineProfile).getOwner();
|
||||
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
|
||||
Mockito.doReturn(templateTag).when(vmTemplateVO).getTemplateTag();
|
||||
Mockito.doReturn(emptyList).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
Mockito.doReturn(emptyList).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
List<Host> suitableHosts = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
|
||||
|
||||
Assert.assertNull(suitableHosts);
|
||||
|
|
@ -208,7 +208,7 @@ public class FirstFitAllocatorTest {
|
|||
Mockito.doReturn(account).when(virtualMachineProfile).getOwner();
|
||||
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
|
||||
Mockito.doReturn(templateTag).when(vmTemplateVO).getTemplateTag();
|
||||
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).allocateTo(Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), Mockito.any(ServiceOffering.class), Mockito.any(VMTemplateVO.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyList(), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.any(Account.class));
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsToAvoidSet(Mockito.any(Host.Type.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
|
||||
List<Host> suitableHosts = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
|
||||
|
|
@ -226,7 +226,7 @@ public class FirstFitAllocatorTest {
|
|||
Mockito.doReturn(account).when(virtualMachineProfile).getOwner();
|
||||
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
|
||||
Mockito.doReturn(templateTag).when(vmTemplateVO).getTemplateTag();
|
||||
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).allocateTo(Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), Mockito.any(ServiceOffering.class), Mockito.any(VMTemplateVO.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyList(), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.any(Account.class));
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsToAvoidSet(Mockito.any(Host.Type.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
|
||||
firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
|
||||
|
|
@ -245,7 +245,8 @@ public class FirstFitAllocatorTest {
|
|||
Mockito.doReturn(hostsWithHaTag).when(hostDaoMock).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.anyString(), Mockito.anyList());
|
||||
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, clusterId, podId, dcId, hostTag, templateTag);
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, vmTemplateVO, clusterId, podId, dcId, hostTag, templateTag);
|
||||
|
||||
Assert.assertEquals(2, resultHosts.size());
|
||||
Assert.assertEquals(host1, resultHosts.get(0));
|
||||
|
|
@ -262,7 +263,8 @@ public class FirstFitAllocatorTest {
|
|||
Mockito.doReturn(hostsWithHaTag).when(hostDaoMock).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.anyString(), Mockito.anyList());
|
||||
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, hostsToFilter, clusterId, podId, dcId, hostTag, templateTag);
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, hostsToFilter, vmTemplateVO, clusterId, podId, dcId, hostTag, templateTag);
|
||||
|
||||
Assert.assertEquals(2, resultHosts.size());
|
||||
Assert.assertEquals(host1, resultHosts.get(0));
|
||||
|
|
@ -278,7 +280,8 @@ public class FirstFitAllocatorTest {
|
|||
Mockito.doReturn(upAndEnabledHostsWithNoHa).when(resourceManagerMock).listAllUpAndEnabledNonHAHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.nullable(String.class), Mockito.anyList());
|
||||
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, clusterId, podId, dcId, null, null);
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, vmTemplateVO, clusterId, podId, dcId, null, null);
|
||||
|
||||
Assert.assertEquals(2, resultHosts.size());
|
||||
Assert.assertEquals(host1, resultHosts.get(0));
|
||||
|
|
@ -293,7 +296,8 @@ public class FirstFitAllocatorTest {
|
|||
Mockito.doNothing().when(firstFitAllocatorSpy).retainHostsMatchingServiceOfferingAndTemplateTags(Mockito.anyList(), Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.anyString(), Mockito.anyList());
|
||||
firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, clusterId, podId, dcId, hostTag, templateTag);
|
||||
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, vmTemplateVO, clusterId, podId, dcId, hostTag, templateTag);
|
||||
|
||||
Mockito.verify(firstFitAllocatorSpy, Mockito.times(1)).retainHostsMatchingServiceOfferingAndTemplateTags(Mockito.anyList(), Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ public class RandomAllocatorTest {
|
|||
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, null, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(1, availableHosts.size());
|
||||
|
|
@ -237,6 +238,7 @@ public class RandomAllocatorTest {
|
|||
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, null, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(2, availableHosts.size());
|
||||
|
|
@ -252,6 +254,7 @@ public class RandomAllocatorTest {
|
|||
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
|
||||
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, hostTag, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(1, availableHosts.size());
|
||||
|
|
@ -267,6 +270,7 @@ public class RandomAllocatorTest {
|
|||
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
|
||||
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, hostTag, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(2, availableHosts.size());
|
||||
|
|
@ -281,6 +285,7 @@ public class RandomAllocatorTest {
|
|||
|
||||
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, null, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(1, availableHosts.size());
|
||||
|
|
@ -295,6 +300,7 @@ public class RandomAllocatorTest {
|
|||
|
||||
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, null, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(2, availableHosts.size());
|
||||
|
|
@ -309,6 +315,7 @@ public class RandomAllocatorTest {
|
|||
|
||||
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
|
||||
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, hostTag, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(1, availableHosts.size());
|
||||
|
|
@ -323,6 +330,7 @@ public class RandomAllocatorTest {
|
|||
|
||||
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
|
||||
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
|
||||
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
|
||||
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, hostTag, clusterId, podId, zoneId);
|
||||
|
||||
Assert.assertEquals(2, availableHosts.size());
|
||||
|
|
|
|||
|
|
@ -75,20 +75,20 @@ public class KeystoreTest extends TestCase {
|
|||
ComponentLocator locator = ComponentLocator.getCurrentLocator();
|
||||
|
||||
KeystoreDao ksDao = locator.getDao(KeystoreDao.class);
|
||||
ksDao.save("CPVMCertificate", "CPVMCertificate", "KeyForCertificate", "realhostip.com");
|
||||
ksDao.save("CPVMCertificate", "CPVMCertificate", "KeyForCertificate", "example.com");
|
||||
ksVo = ksDao.findByName("CPVMCertificate");
|
||||
assertTrue(ksVo != null);
|
||||
assertTrue(ksVo.getCertificate().equals("CPVMCertificate"));
|
||||
assertTrue(ksVo.getKey().equals("KeyForCertificate"));
|
||||
assertTrue(ksVo.getDomainSuffix().equals("realhostip.com"));
|
||||
assertTrue(ksVo.getDomainSuffix().equals("example.com"));
|
||||
|
||||
ksDao.save("CPVMCertificate", "CPVMCertificate Again", "KeyForCertificate Again", "again.realhostip.com");
|
||||
ksDao.save("CPVMCertificate", "CPVMCertificate Again", "KeyForCertificate Again", "again.example.com");
|
||||
|
||||
ksVo = ksDao.findByName("CPVMCertificate");
|
||||
assertTrue(ksVo != null);
|
||||
assertTrue(ksVo.getCertificate().equals("CPVMCertificate Again"));
|
||||
assertTrue(ksVo.getKey().equals("KeyForCertificate Again"));
|
||||
assertTrue(ksVo.getDomainSuffix().equals("again.realhostip.com"));
|
||||
assertTrue(ksVo.getDomainSuffix().equals("again.example.com"));
|
||||
|
||||
ksDao.expunge(ksVo.getId());
|
||||
}
|
||||
|
|
@ -112,9 +112,9 @@ public class KeystoreTest extends TestCase {
|
|||
assertTrue(ksMgr.configure("TaskManager", new HashMap<String, Object>()));
|
||||
assertTrue(ksMgr.start());
|
||||
|
||||
ksMgr.saveCertificate("CPVMCertificate", certContent, keyContent, "realhostip.com");
|
||||
ksMgr.saveCertificate("CPVMCertificate", certContent, keyContent, "example.com");
|
||||
|
||||
byte[] ksBits = ksMgr.getKeystoreBits("CPVMCertificate", "realhostip", "vmops.com");
|
||||
byte[] ksBits = ksMgr.getKeystoreBits("CPVMCertificate", "example", "vmops.com");
|
||||
assertTrue(ksBits != null);
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@
|
|||
<resource>
|
||||
<directory>certs</directory>
|
||||
<excludes>
|
||||
<exclude>realhostip.csr</exclude>
|
||||
<exclude>systemvm.csr</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@
|
|||
<systemProperties>
|
||||
<systemProperty>
|
||||
<key>javax.net.ssl.trustStore</key>
|
||||
<value>certs/realhostip.keystore</value>
|
||||
<value>certs/systemvm.keystore</value>
|
||||
<key>log.home</key>
|
||||
<value>${PWD}/</value>
|
||||
</systemProperty>
|
||||
|
|
|
|||
|
|
@ -60,4 +60,4 @@ if [ "$(uname -m | grep '64')" == "" ]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
java -Djavax.net.ssl.trustStore=./certs/realhostip.keystore -Djdk.tls.ephemeralDHKeySize=2048 -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@
|
||||
java -Djavax.net.ssl.trustStore=./certs/systemvm.keystore -Djdk.tls.ephemeralDHKeySize=2048 -Dlog.home=$LOGHOME -mx${maxmem}m -cp $CP com.cloud.agent.AgentShell $keyvalues $@
|
||||
|
|
|
|||
|
|
@ -52,13 +52,13 @@ cflag=
|
|||
cpkflag=
|
||||
cpcflag=
|
||||
cccflag=
|
||||
customPrivKey=$(dirname $0)/certs/realhostip.key
|
||||
customPrivCert=$(dirname $0)/certs/realhostip.crt
|
||||
customPrivKey=$(dirname $0)/certs/systemvm.key
|
||||
customPrivCert=$(dirname $0)/certs/systemvm.crt
|
||||
customCertChain=
|
||||
customCACert=
|
||||
publicIp=
|
||||
hostName=
|
||||
keyStore=$(dirname $0)/certs/realhostip.keystore
|
||||
keyStore=$(dirname $0)/certs/systemvm.keystore
|
||||
defaultJavaKeyStoreFile=/etc/ssl/certs/java/cacerts
|
||||
defaultJavaKeyStorePass="changeit"
|
||||
aliasName="CPVMCertificate"
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ patch_systemvm() {
|
|||
fi
|
||||
rm -fr $backupfolder
|
||||
# Import global cacerts into 'cloud' service's keystore
|
||||
keytool -importkeystore -srckeystore /etc/ssl/certs/java/cacerts -destkeystore /usr/local/cloud/systemvm/certs/realhostip.keystore -srcstorepass changeit -deststorepass vmops.com -noprompt || true
|
||||
keytool -importkeystore -srckeystore /etc/ssl/certs/java/cacerts -destkeystore /usr/local/cloud/systemvm/certs/systemvm.keystore -srcstorepass changeit -deststorepass vmops.com -noprompt || true
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,25 +126,25 @@ patch_systemvm() {
|
|||
|
||||
if [ "$TYPE" = "consoleproxy" ] || [ "$TYPE" = "secstorage" ]; then
|
||||
# Import global cacerts into 'cloud' service's keystore
|
||||
REALHOSTIP_KS_FILE="/usr/local/cloud/systemvm/certs/realhostip.keystore"
|
||||
REALHOSTIP_PASS="vmops.com"
|
||||
SYSTEMVM_KS_FILE="/usr/local/cloud/systemvm/certs/systemvm.keystore"
|
||||
SYSTEMVM_PASS="vmops.com"
|
||||
|
||||
keytool -importkeystore -srckeystore /etc/ssl/certs/java/cacerts \
|
||||
-destkeystore "$REALHOSTIP_KS_FILE" -srcstorepass changeit -deststorepass \
|
||||
"$REALHOSTIP_PASS" -noprompt 2>/dev/null || true
|
||||
-destkeystore "$SYSTEMVM_KS_FILE" -srcstorepass changeit -deststorepass \
|
||||
"$SYSTEMVM_PASS" -noprompt 2>/dev/null || true
|
||||
|
||||
# Import CA cert(s) into realhostip.keystore so the SSVM JVM
|
||||
# Import CA cert(s) into systemvm.keystore so the SSVM JVM
|
||||
# (which overrides the truststore via -Djavax.net.ssl.trustStore in _run.sh)
|
||||
# can trust servers signed by the CloudStack CA
|
||||
CACERT_FILE="/usr/local/share/ca-certificates/cloudstack/ca.crt"
|
||||
|
||||
if [ -f "$CACERT_FILE" ] && [ -f "$REALHOSTIP_KS_FILE" ]; then
|
||||
if [ -f "$CACERT_FILE" ] && [ -f "$SYSTEMVM_KS_FILE" ]; then
|
||||
awk 'BEGIN{n=0} /-----BEGIN CERTIFICATE-----/{n++} n>0{print > "cloudca." n }' "$CACERT_FILE"
|
||||
for caChain in $(ls cloudca.* 2>/dev/null); do
|
||||
keytool -delete -noprompt -alias "$caChain" -keystore "$REALHOSTIP_KS_FILE" \
|
||||
-storepass "$REALHOSTIP_PASS" > /dev/null 2>&1 || true
|
||||
keytool -delete -noprompt -alias "$caChain" -keystore "$SYSTEMVM_KS_FILE" \
|
||||
-storepass "$SYSTEMVM_PASS" > /dev/null 2>&1 || true
|
||||
keytool -import -noprompt -trustcacerts -alias "$caChain" -file "$caChain" \
|
||||
-keystore "$REALHOSTIP_KS_FILE" -storepass "$REALHOSTIP_PASS" > /dev/null 2>&1
|
||||
-keystore "$SYSTEMVM_KS_FILE" -storepass "$SYSTEMVM_PASS" > /dev/null 2>&1
|
||||
done
|
||||
rm -f cloudca.*
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@
|
|||
<systemProperties>
|
||||
<systemProperty>
|
||||
<key>javax.net.ssl.trustStore</key>
|
||||
<value>certs/realhostip.keystore</value>
|
||||
<value>certs/systemvm.keystore</value>
|
||||
<key>log.home</key>
|
||||
<value>${PWD}/</value>
|
||||
</systemProperty>
|
||||
|
|
|
|||
|
|
@ -1252,6 +1252,8 @@
|
|||
"label.guestnetwork": "Guest Network",
|
||||
"label.guestnetworkid": "Network ID",
|
||||
"label.guestnetworkname": "Network name",
|
||||
"label.guestosasrule": "Guest OS as rule",
|
||||
"label.guestosrule": "Guest OS rule",
|
||||
"label.gueststartip": "Guest start IP",
|
||||
"label.guest.vlan": "Guest VLAN",
|
||||
"label.guestvlanrange": "VLAN range(s)",
|
||||
|
|
|
|||
|
|
@ -1090,6 +1090,8 @@
|
|||
"label.guestnetwork": "Rede guest",
|
||||
"label.guestnetworkid": "ID de rede",
|
||||
"label.guestnetworkname": "Nome da rede",
|
||||
"label.guestosasrule": "SO guest como regra JS",
|
||||
"label.guestosrule": "Regra SO guest",
|
||||
"label.gueststartip": "IP de in\u00edcio do guest",
|
||||
"label.guestvlanrange": "Intervalo(s) de VLAN",
|
||||
"label.guestvmcidr": "CIDR",
|
||||
|
|
|
|||
|
|
@ -64,7 +64,22 @@
|
|||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="oscategoryid" ref="oscategoryid">
|
||||
<a-form-item name="guestosasrule" ref="guestosasrule">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.guestosasrule')"/>
|
||||
</template>
|
||||
<a-switch v-model:checked="form.guestosasrule"/>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.guestosasrule" name="guestosrule" ref="guestosrule">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.guestosrule')" :tooltip="apiParams.guestosrule.description"/>
|
||||
</template>
|
||||
<a-textarea
|
||||
v-model:value="form.guestosrule"
|
||||
:placeholder="apiParams.guestosrule.name">
|
||||
</a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="!form.guestosasrule" name="oscategoryid" ref="oscategoryid">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.oscategoryid')" :tooltip="apiParams.oscategoryid.description"/>
|
||||
</template>
|
||||
|
|
@ -149,6 +164,7 @@ export default {
|
|||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
const guestOsRule = this.resource?.guestosrule
|
||||
this.form = reactive({
|
||||
name: this.resource.name,
|
||||
hosttags: this.resource.explicithosttags,
|
||||
|
|
@ -157,7 +173,9 @@ export default {
|
|||
? this.resource.storageaccessgroups.split(',')
|
||||
: [],
|
||||
oscategoryid: this.resource.oscategoryid,
|
||||
externaldetails: this.resourceExternalDetails
|
||||
externaldetails: this.resourceExternalDetails,
|
||||
guestosasrule: guestOsRule !== undefined,
|
||||
guestosrule: guestOsRule
|
||||
})
|
||||
this.rules = reactive({})
|
||||
},
|
||||
|
|
@ -194,7 +212,11 @@ export default {
|
|||
params.id = this.resource.id
|
||||
params.name = values.name
|
||||
params.hosttags = values.hosttags
|
||||
params.oscategoryid = values.oscategoryid
|
||||
if (values.guestosasrule === true) {
|
||||
params.guestosrule = values.guestosrule
|
||||
} else {
|
||||
params.oscategoryid = values.oscategoryid || this.osCategories.opts.filter(os => os.name === 'None')[0]?.id
|
||||
}
|
||||
if (values.istagarule !== undefined) {
|
||||
params.istagarule = values.istagarule
|
||||
}
|
||||
|
|
@ -205,9 +227,11 @@ export default {
|
|||
} else {
|
||||
params.cleanupexternaldetails = true
|
||||
}
|
||||
|
||||
Object.keys(params).forEach((key) => (params[key] == null) && delete params[key])
|
||||
this.loading = true
|
||||
|
||||
postAPI('updateHost', params).then(json => {
|
||||
postAPI('updateHost', params).then(() => {
|
||||
this.$message.success({
|
||||
content: `${this.$t('label.action.update.host')} - ${values.name}`,
|
||||
duration: 2
|
||||
|
|
|
|||
|
|
@ -16,41 +16,60 @@
|
|||
//under the License.
|
||||
package org.apache.cloudstack.utils.jsinterpreter;
|
||||
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.utils.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
public class GenericRuleHelper {
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
protected static Logger LOGGER = LogManager.getLogger(GenericRuleHelper.class);
|
||||
|
||||
public class TagAsRuleHelper {
|
||||
|
||||
protected static Logger LOGGER = LogManager.getLogger(TagAsRuleHelper.class);
|
||||
|
||||
public static boolean interpretTagAsRule(String rule, String tags, long timeout) {
|
||||
public static boolean interpretTagAsRule(String rule, String tags, long timeout, String configName) {
|
||||
List<String> tagsPresetVariable = new ArrayList<>();
|
||||
if (!StringUtils.isEmpty(tags)) {
|
||||
tagsPresetVariable.addAll(Arrays.asList(tags.split(",")));
|
||||
}
|
||||
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(timeout)) {
|
||||
jsInterpreter.injectVariable("tags", tagsPresetVariable);
|
||||
Object scriptReturn = jsInterpreter.executeScript(rule);
|
||||
if (scriptReturn instanceof Boolean) {
|
||||
return (Boolean)scriptReturn;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
String message = String.format("Error while executing script [%s].", rule);
|
||||
LOGGER.error(message, ex);
|
||||
throw new CloudRuntimeException(message, ex);
|
||||
Boolean scriptReturn = interpretRule("tags", tagsPresetVariable, timeout, rule, configName);
|
||||
|
||||
if (scriptReturn != null) {
|
||||
return scriptReturn;
|
||||
}
|
||||
|
||||
LOGGER.debug("Result of tag rule [{}] was not a boolean, returning false.", rule);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean interpretGuestOsRule(String rule, String vmGuestOs, long timeout, String configName) {
|
||||
Boolean scriptReturn = interpretRule("vmGuestOs", vmGuestOs, timeout, rule, configName);
|
||||
|
||||
if (scriptReturn != null) {
|
||||
return scriptReturn;
|
||||
}
|
||||
|
||||
LOGGER.debug("Result of guest OS rule [{}] was not a boolean, returning false.", rule);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Boolean interpretRule(String variableName, Object variableValue, long timeout, String script, String configName) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(timeout, configName)) {
|
||||
jsInterpreter.injectVariable(variableName, variableValue);
|
||||
Object scriptReturn = jsInterpreter.executeScript(script);
|
||||
if (scriptReturn instanceof Boolean) {
|
||||
return (Boolean) scriptReturn;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
String message = String.format("Error while executing script [%s].", script);
|
||||
LOGGER.error(message, ex);
|
||||
throw new CloudRuntimeException(message, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -86,10 +86,10 @@ public class JsInterpreter implements Closeable {
|
|||
*/
|
||||
protected JsInterpreter() { }
|
||||
|
||||
public JsInterpreter(long timeout) {
|
||||
public JsInterpreter(long timeout, String configName) {
|
||||
this.timeout = timeout;
|
||||
this.timeoutDefaultMessage = String.format(
|
||||
"Timeout (in milliseconds) defined in the global setting [quota.activationrule.timeout]: [%s].", this.timeout);
|
||||
this.timeoutDefaultMessage = String.format("Timeout (in milliseconds) defined in the global setting [%s]: [%s].",
|
||||
configName, this.timeout);
|
||||
|
||||
if (System.getProperty("nashorn.args") == null) {
|
||||
System.setProperty("nashorn.args", "--no-java --no-syntax-extensions");
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class ImageStoreUtilTest {
|
|||
|
||||
@Test
|
||||
public void testgenerateHttpsPostUploadUrl() throws MalformedURLException {
|
||||
String ssvmdomain = "*.realhostip.com";
|
||||
String ssvmdomain = "*.example.com";
|
||||
String ipAddress = "10.147.28.14";
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String protocol = "https";
|
||||
|
|
@ -47,7 +47,7 @@ public class ImageStoreUtilTest {
|
|||
|
||||
@Test
|
||||
public void testgenerateHttpPostUploadUrl() throws MalformedURLException {
|
||||
String ssvmdomain = "*.realhostip.com";
|
||||
String ssvmdomain = "*.example.com";
|
||||
String ipAddress = "10.147.28.14";
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String protocol = "http";
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ public class JsInterpreterTest {
|
|||
|
||||
@Test
|
||||
public void executeScriptTestValidScriptShouldPassWithMixedVariables() {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(1000)) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(1000, "timeout.configuration")) {
|
||||
jsInterpreter.injectVariable("x", 10);
|
||||
jsInterpreter.injectVariable("y", "hello");
|
||||
jsInterpreter.injectVariable("z", true);
|
||||
|
|
@ -174,7 +174,7 @@ public class JsInterpreterTest {
|
|||
}
|
||||
|
||||
private void runMaliciousScriptFileTest(String script, String filename) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(1000)) {
|
||||
try (JsInterpreter jsInterpreter = new JsInterpreter(1000, "timeout.configuration")) {
|
||||
jsInterpreter.executeScript(script);
|
||||
} catch (CloudRuntimeException ex) {
|
||||
Assert.assertTrue(ex.getMessage().contains("Unable to execute script"));
|
||||
|
|
|
|||
Loading…
Reference in New Issue