diff --git a/.asf.yaml b/.asf.yaml
index 15bf55ab3b94..cb957e57fd95 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -16,7 +16,6 @@
# under the License.
# https://cwiki.apache.org/confluence/display/INFRA/git+-+.asf.yaml+features
----
github:
description: "Apache CloudStack is an opensource Infrastructure as a Service (IaaS) cloud computing platform"
homepage: https://cloudstack.apache.org/
@@ -50,20 +49,34 @@ github:
rebase: false
collaborators:
- - acs-robot
- - rajujith
- - winterhazel
- - gpordeus
- - hsato03
- - bernardodemarco
- - abh1sar
- - FelipeM525
- - lucas-a-martins
+ - ingox
+ - gp-santos
+ - erikbocks
+ - Imvedansh
+ - Damans227
+ - jmsperu
+ - GaOrtiga
+ - bhouse-nexthop
- protected_branches: ~
+ rulesets:
+ - name: "Default Branch Protection"
+ type: branch
+ branches:
+ includes:
+ - "~DEFAULT_BRANCH"
+ excludes: []
+ bypass_teams:
+ - root
+ restrict_deletion: true
+ restrict_force_push: true
+
+ copilot_code_review:
+ enabled: true
+ review_drafts: true
+ review_on_push: true
notifications:
- commits: commits@cloudstack.apache.org
- issues: commits@cloudstack.apache.org
+ commits: commits@cloudstack.apache.org
+ issues: commits@cloudstack.apache.org
pullrequests: commits@cloudstack.apache.org
- discussions: users@cloudstack.apache.org
+ discussions: users@cloudstack.apache.org
diff --git a/.codespellrc b/.codespellrc
new file mode 100644
index 000000000000..3c632f8ba534
--- /dev/null
+++ b/.codespellrc
@@ -0,0 +1,20 @@
+# 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.
+
+[codespell]
+ignore-words = .github/linters/codespell.txt
+skip = systemvm/agent/noVNC/*,ui/package.json,ui/package-lock.json,ui/public/js/less.min.js,ui/public/locales/*.json,server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java,test/integration/smoke/test_ssl_offloading.py
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000000..1b06f3ebf53c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+.github/workflows/*.lock.yml linguist-generated=true merge=ours
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000000..375912667d63
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,25 @@
+# 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.
+
+/plugins/storage/volume/linstor @rp-
+/plugins/storage/volume/storpool @slavkap
+/plugins/storage/volume/ontap @rajiv-jain-netapp @sandeeplocharla @piyush5netapp @suryag1201
+
+.pre-commit-config.yaml @jbampton
+/.github/linters/ @jbampton
+
+/plugins/network-elements/nsx/ @Pearl1594 @nvazquez
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/feature_request.yaml
rename to .github/ISSUE_TEMPLATE/feature_request.yml
diff --git a/.github/actions/install-nonoss/action.yml b/.github/actions/install-nonoss/action.yml
new file mode 100644
index 000000000000..39a03213c29d
--- /dev/null
+++ b/.github/actions/install-nonoss/action.yml
@@ -0,0 +1,31 @@
+# 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.
+
+name: 'Install CloudStack Non-OSS'
+description: 'Clones and installs the shapeblue/cloudstack-nonoss repository.'
+
+runs:
+ using: "composite"
+ steps:
+ - name: Install cloudstack-nonoss
+ shell: bash
+ run: |
+ git clone --depth 1 https://github.com/shapeblue/cloudstack-nonoss.git nonoss
+ cd nonoss
+ bash -x install-non-oss.sh
+ cd ..
+ rm -fr nonoss
diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml
new file mode 100644
index 000000000000..0f8425229242
--- /dev/null
+++ b/.github/actions/setup-env/action.yml
@@ -0,0 +1,58 @@
+# 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.
+
+name: 'Setup CloudStack Environment'
+description: 'Sets up JDK (with Maven cache), optionally Python, and optionally APT build dependencies for CloudStack.'
+
+inputs:
+ java-version:
+ description: 'The JDK version to use'
+ required: false
+ default: '17'
+ install-python:
+ description: 'Whether to install Python 3.10'
+ required: false
+ default: 'false'
+ install-apt-deps:
+ description: 'Whether to install CloudStack APT build dependencies'
+ required: false
+ default: 'false'
+
+runs:
+ using: "composite"
+ steps:
+ - name: Set up JDK ${{ inputs.java-version }}
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
+ with:
+ java-version: ${{ inputs.java-version }}
+ distribution: 'adopt'
+ architecture: x64
+ cache: 'maven'
+
+ - name: Set up Python
+ if: ${{ inputs.install-python == 'true' }}
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ with:
+ python-version: '3.10'
+ architecture: x64
+
+ - name: Install Build Dependencies
+ if: ${{ inputs.install-apt-deps == 'true' }}
+ shell: bash
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y git uuid-runtime genisoimage netcat-openbsd ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools
diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json
new file mode 100644
index 000000000000..ea25ffab6b9e
--- /dev/null
+++ b/.github/aw/actions-lock.json
@@ -0,0 +1,56 @@
+{
+ "entries": {
+ "actions/github-script@v9.0.0": {
+ "repo": "actions/github-script",
+ "version": "v9.0.0",
+ "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3"
+ },
+ "github/gh-aw-actions/setup@v0.76.1": {
+ "repo": "github/gh-aw-actions/setup",
+ "version": "v0.76.1",
+ "sha": "46d564922b082d0db93244972e8005ea6904ee5f"
+ }
+ },
+ "containers": {
+ "ghcr.io/github/gh-aw-firewall/agent:0.18.0": {
+ "image": "ghcr.io/github/gh-aw-firewall/agent:0.18.0",
+ "digest": "sha256:ab84dfc7f5998cb8cd0c596526dd573b7e7d06c6a740266a1e6df879fa16c866",
+ "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.18.0@sha256:ab84dfc7f5998cb8cd0c596526dd573b7e7d06c6a740266a1e6df879fa16c866"
+ },
+ "ghcr.io/github/gh-aw-firewall/agent:0.25.55": {
+ "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"
+ },
+ "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55": {
+ "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"
+ },
+ "ghcr.io/github/gh-aw-firewall/squid:0.18.0": {
+ "image": "ghcr.io/github/gh-aw-firewall/squid:0.18.0",
+ "digest": "sha256:82a5d062a5612a57a43a171a5b79ddbb690a86a8ddda02339cc1675131ae9f8b",
+ "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.18.0@sha256:82a5d062a5612a57a43a171a5b79ddbb690a86a8ddda02339cc1675131ae9f8b"
+ },
+ "ghcr.io/github/gh-aw-firewall/squid:0.25.55": {
+ "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"
+ },
+ "ghcr.io/github/gh-aw-mcpg:v0.1.4": {
+ "image": "ghcr.io/github/gh-aw-mcpg:v0.1.4",
+ "digest": "sha256:0acf25aa1d409f9c73be9e39ac84f4bd4b90d8bfa1db4dc6d7f47d38ccd58914",
+ "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.1.4@sha256:0acf25aa1d409f9c73be9e39ac84f4bd4b90d8bfa1db4dc6d7f47d38ccd58914"
+ },
+ "ghcr.io/github/gh-aw-mcpg:v0.3.19": {
+ "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"
+ },
+ "ghcr.io/github/github-mcp-server:v0.30.3": {
+ "image": "ghcr.io/github/github-mcp-server:v0.30.3",
+ "digest": "sha256:a2b5fb79b1cee851bfc3532dfe480c3dc5736974ca9d93a7a9f68e52ce4b62a0",
+ "pinned_image": "ghcr.io/github/github-mcp-server:v0.30.3@sha256:a2b5fb79b1cee851bfc3532dfe480c3dc5736974ca9d93a7a9f68e52ce4b62a0"
+ }
+ }
+}
diff --git a/.github/aw/imports/.gitattributes b/.github/aw/imports/.gitattributes
new file mode 100644
index 000000000000..f0516fad90e4
--- /dev/null
+++ b/.github/aw/imports/.gitattributes
@@ -0,0 +1,5 @@
+# Mark all cached import files as generated
+* linguist-generated=true
+
+# Use 'ours' merge strategy to keep local cached versions
+* merge=ours
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000000..6ffb926f6fa0
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,43 @@
+# 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.
+
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ open-pull-requests-limit: 2
+ schedule:
+ interval: "weekly"
+ groups:
+ github-actions-dependencies:
+ patterns:
+ - "*"
+ ignore:
+ - dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
+ cooldown:
+ default-days: 7
+ - package-ecosystem: "maven"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ cooldown:
+ default-days: 7
diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml
index 5e8d65905eb6..6a8a26f48497 100644
--- a/.github/linters/.markdown-lint.yml
+++ b/.github/linters/.markdown-lint.yml
@@ -18,9 +18,6 @@
# MD001/heading-increment Heading levels should only increment by one level at a time
MD001: false
-# MD003/heading-style Heading style
-MD003: false
-
# MD004/ul-style Unordered list style
MD004: false
@@ -86,3 +83,6 @@ MD046: false
# MD052/reference-links-images Reference links and images should use a label that is defined
MD052: false
+
+# MD059/descriptive-link-text Link text should be descriptive
+MD059: false
diff --git a/.github/linters/.yamllint.yml b/.github/linters/.yamllint.yml
new file mode 100644
index 000000000000..97b66848696a
--- /dev/null
+++ b/.github/linters/.yamllint.yml
@@ -0,0 +1,32 @@
+# 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.
+---
+extends: default
+
+rules:
+ line-length:
+ max: 400 # Very forgiving for GitHub Actions and infrastructure files
+ indentation: disable # Disable indentation checking for existing files
+ comments: disable # Disable comment formatting checks
+ braces: disable
+ brackets: disable # Disable bracket spacing checks
+ colons:
+ max-spaces-after: -1 # Allow any number of spaces after colon
+ max-spaces-before: 0
+ document-start: disable # Many files don't have ---
+ truthy:
+ allowed-values: ['true', 'false', 'on', 'off', 'yes', 'no']
diff --git a/.github/linters/codespell.txt b/.github/linters/codespell.txt
index 37b3e6de1cbe..67cbeaa7cbb3 100644
--- a/.github/linters/codespell.txt
+++ b/.github/linters/codespell.txt
@@ -4,6 +4,7 @@ acount
actuall
acuiring
acumulate
+addin
addreess
addtion
adminstrator
@@ -12,10 +13,8 @@ afrer
afterall
againt
ags
-aktive
algoritm
allo
-alloacate
allocted
alocation
alogrithm
@@ -65,6 +64,7 @@ bject
boardcast
bootstraper
bu
+callin
cant
capabilites
capablity
@@ -73,6 +73,7 @@ carrefully
cavaet
chaing
checkd
+checkin
childs
choosen
chould
@@ -93,7 +94,6 @@ confg
configruation
configuable
conneciton
-connexion
constrait
constraits
containg
@@ -101,9 +101,7 @@ contex
continuesly
contro
controler
-controles
controll
-convienient
convinience
coputer
correcponding
@@ -158,13 +156,13 @@ differnet
differnt
direcotry
directroy
-disale
disbale
discrepency
disover
dissapper
dissassociated
divice
+dockin
doesn'
doesnot
doesnt
@@ -175,7 +173,6 @@ eanbled
earch
ect
elemnt
-eles
elments
emmited
enble
@@ -183,29 +180,23 @@ encryted
enebled
enmpty
entires
-enviornment
environmnet
equivalant
erro
erronous
-everthing
everytime
-excetion
-excption
excute
execept
execption
+exects
execut
executeable
exeeded
exisitng
exisits
-existin
existsing
-exitting
expcted
expection
-explaination
explicitely
faield
faild
@@ -218,7 +209,6 @@ fillled
findout
fisrt
fo
-folowing
fowarding
frist
fro
@@ -237,6 +227,7 @@ hanling
happend
hasing
hasnt
+havin
hda
hostanme
hould
@@ -256,20 +247,14 @@ implmeneted
implmentation
incase
includeing
-incosistency
indecates
-indien
infor
informations
informaton
-infrastrcuture
ingore
-inital
initalize
initator
-initilization
inspite
-instace
instal
instnace
intefaces
@@ -287,12 +272,8 @@ ist
klunky
lable
leve
-lief
limite
-linke
listner
-lokal
-lokales
maintainence
maintenace
maintenence
@@ -301,7 +282,6 @@ mambers
manaully
manuel
maxium
-mehtod
mergable
mesage
messge
@@ -311,7 +291,6 @@ minumum
mis
modifers
mor
-mot
mulitply
multipl
multple
@@ -325,7 +304,7 @@ nin
nodel
nome
noone
-nowe
+notin
numbe
numer
occured
@@ -378,6 +357,7 @@ propogate
provison
psudo
pyhsical
+re-use
readabilty
readd
reccuring
@@ -392,12 +372,9 @@ remaning
remore
remvoing
renabling
-repeatly
reponse
reqest
reqiured
-requieres
-requried
reserv
reserverd
reseted
@@ -414,17 +391,15 @@ retriving
retrun
retuned
returing
-re-use
rever
rocessor
+roperty
runing
runnign
sate
scalled
-scipt
scirpt
scrip
-seconadry
seconday
seesion
sepcified
@@ -437,12 +412,10 @@ settig
sevices
shoul
shoule
-sie
signle
simplier
singature
skiping
-snaphsot
snpashot
specied
specifed
@@ -453,7 +426,6 @@ standy
statics
stickyness
stil
-stip
storeage
strat
streched
@@ -462,7 +434,6 @@ succesfull
successfull
suceessful
suces
-sucessfully
suiteable
suppots
suppport
@@ -495,7 +466,6 @@ uncompressible
uneccessarily
unexepected
unexpect
-unknow
unkonw
unkown
unneccessary
@@ -503,14 +473,12 @@ unparseable
unrecoginized
unsupport
unxpected
-updat
uptodate
usera
usign
usin
utlization
vaidate
-valiate
valule
valus
varibles
@@ -519,8 +487,6 @@ verfying
verifing
virutal
visable
-wakup
wil
wit
-wll
wth
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fd3c8f8ac67d..9c8ca10f85e9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -16,41 +16,31 @@
# under the License.
name: Build
-
-on: [push, pull_request]
-
+on:
+ push:
+ branches:
+ - main
+ - 4.22
+ - 4.20
+ pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
-
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
permissions:
contents: read
-
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v4
-
- - name: Set up JDK 11
- uses: actions/setup-java@v4
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
- java-version: '11'
- distribution: 'adopt'
- architecture: x64
- cache: maven
+ persist-credentials: false
- - name: Set up Python
- uses: actions/setup-python@v5
+ - name: Setup Environment
+ uses: ./.github/actions/setup-env
with:
- python-version: '3.10'
- architecture: 'x64'
-
- - name: Install Build Dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y git uuid-runtime genisoimage netcat ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 ipmitool libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools
-
+ install-python: 'true'
+ install-apt-deps: 'true'
- name: Env details
run: |
uname -a
@@ -61,9 +51,8 @@ jobs:
free -m
nproc
git status
-
+ - name: Install Non-OSS
+ uses: ./.github/actions/install-nonoss
- name: Noredist Build
run: |
- git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss && cd nonoss && bash -x install-non-oss.sh && cd ..
- rm -fr nonoss
mvn -B -P developer,systemvm -Dsimulator -Dnoredist clean install -T$(nproc)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ceffb42c79bb..7f6645f4af4d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,21 +16,60 @@
# under the License.
name: Simulator CI
-
-on: [push, pull_request]
-
+on:
+ push:
+ branches:
+ - main
+ - 4.22
+ - 4.20
+ pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
-
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
permissions:
contents: read
-
jobs:
build:
if: github.repository == 'apache/cloudstack'
- runs-on: ubuntu-22.04
-
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ fetch-depth: 0
+ persist-credentials: false
+ - name: Setup Environment
+ uses: ./.github/actions/setup-env
+ with:
+ install-python: 'true'
+ install-apt-deps: 'true'
+ - name: Env details
+ run: |
+ uname -a
+ whoami
+ javac -version
+ mvn -v
+ python3 --version
+ free -m
+ nproc
+ git status
+ ipmitool -V
+ - name: Build with Maven
+ run: |
+ mvn -B -P developer,systemvm -Dsimulator clean install -DskipTests=true -T$(nproc)
+ - name: Archive artifacts
+ run: |
+ mkdir -p /tmp/artifacts
+ tar -czf /tmp/artifacts/targets.tar.gz $(find . -name "target" -type d) tools/marvin/dist engine/schema/dist utils/conf
+ tar -czf /tmp/artifacts/m2-cloudstack.tar.gz -C ~/.m2/repository org/apache/cloudstack
+ - name: Upload artifacts
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: build-artifacts
+ path: /tmp/artifacts/
+ test:
+ needs: build
+ if: github.repository == 'apache/cloudstack'
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
@@ -89,7 +128,10 @@ jobs:
smoke/test_nested_virtualization
smoke/test_set_sourcenat
smoke/test_webhook_lifecycle
- smoke/test_purge_expunged_vms",
+ smoke/test_purge_expunged_vms
+ smoke/test_extension_lifecycle
+ smoke/test_extension_custom_action_lifecycle
+ smoke/test_extension_custom",
"smoke/test_network
smoke/test_network_acl
smoke/test_network_ipv6
@@ -137,11 +179,13 @@ jobs:
smoke/test_vm_deployment_planner
smoke/test_vm_strict_host_tags
smoke/test_vm_schedule
+ smoke/test_deploy_vgpu_enabled_vm
smoke/test_vm_life_cycle
smoke/test_vm_lifecycle_unmanage_import
smoke/test_vm_snapshot_kvm
smoke/test_vm_snapshots
smoke/test_volumes
+ smoke/test_vpc_conserve_mode
smoke/test_vpc_ipv6
smoke/test_vpc_redundant
smoke/test_vpc_router_nics
@@ -164,7 +208,8 @@ jobs:
component/test_cpu_limits
component/test_cpu_max_limits
component/test_cpu_project_limits
- component/test_deploy_vm_userdata_multi_nic",
+ component/test_deploy_vm_userdata_multi_nic
+ component/test_deploy_vm_lease",
"component/test_egress_fw_rules
component/test_invalid_gw_nm
component/test_ip_reservation",
@@ -209,79 +254,70 @@ jobs:
smoke/test_list_service_offerings
smoke/test_list_storage_pools
smoke/test_list_volumes"]
-
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
-
- - name: Set up JDK
- uses: actions/setup-java@v4
- with:
- java-version: '11'
- distribution: 'adopt'
- architecture: x64
- cache: maven
-
- - name: Set up Python
- uses: actions/setup-python@v5
+ persist-credentials: false
+ - name: Setup Environment
+ uses: ./.github/actions/setup-env
with:
- python-version: '3.10'
- architecture: 'x64'
-
- - name: Install Build Dependencies
+ install-python: 'true'
+ install-apt-deps: 'true'
+ - name: Setup IPMI Tool for CloudStack
run: |
- sudo apt-get update
- sudo apt-get install -y git uuid-runtime genisoimage netcat ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 ipmitool libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools
+ # Create cloudstack-common directory if it doesn't exist
+ sudo mkdir -p /usr/share/cloudstack-common
+ # Copy ipmitool to cloudstack-common directory if it doesn't exist
+ if [ ! -f /usr/share/cloudstack-common/ipmitool ]; then
+ sudo cp /usr/bin/ipmitool /usr/share/cloudstack-common/ipmitool
+ sudo chmod 755 /usr/share/cloudstack-common/ipmitool
+ fi
+
+ # Create ipmitool-C3 wrapper script
+ sudo tee /usr/bin/ipmitool > /dev/null << 'EOF'
+ #!/bin/bash
+ /usr/share/cloudstack-common/ipmitool -C3 $@
+ EOF
+ sudo chmod 755 /usr/bin/ipmitool
- name: Install Python dependencies
run: |
- python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycrypto mock flask netaddr pylint pycodestyle six astroid
-
+ python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycryptodome mock flask netaddr pylint pycodestyle six astroid pynose
- name: Install jacoco dependencies
run: |
wget https://github.com/jacoco/jacoco/releases/download/v0.8.10/jacoco-0.8.10.zip
unzip jacoco-0.8.10.zip -d jacoco
-
- - name: Env details
- run: |
- uname -a
- whoami
- javac -version
- mvn -v
- python3 --version
- free -m
- nproc
- git status
- ipmitool -V
-
- name: Setup MySQL Server
run: |
# https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md#mysql
sudo apt-get install -y mysql-server
sudo systemctl start mysql
- sudo mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY ''; FLUSH PRIVILEGES;"
+ sudo mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY ''; FLUSH PRIVILEGES;"
sudo systemctl restart mysql
sudo mysql -uroot -e "SELECT VERSION();"
-
- - name: Build with Maven
+ - name: Download artifacts
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: build-artifacts
+ path: /tmp/artifacts/
+ - name: Extract artifacts
run: |
- mvn -B -P developer,systemvm -Dsimulator clean install -DskipTests=true -T$(nproc)
-
+ tar -xzf /tmp/artifacts/targets.tar.gz
+ mkdir -p ~/.m2/repository
+ tar -xzf /tmp/artifacts/m2-cloudstack.tar.gz -C ~/.m2/repository
- name: Setup Simulator Prerequisites
run: |
sudo python3 -m pip install --upgrade netaddr mysql-connector-python
- python3 -m pip install --user --upgrade tools/marvin/dist/Marvin-*.tar.gz
+ python3 -m pip install --user --upgrade tools/marvin/dist/[mM]arvin-*.tar.gz
mvn -q -Pdeveloper -pl developer -Ddeploydb
mvn -q -Pdeveloper -pl developer -Ddeploydb-simulator
-
- name: Generate jacoco-coverage.sh
run: |
echo "java -jar jacoco/lib/jacococli.jar report jacoco-it.exec \\" > jacoco-report.sh
find . | grep "target/classes" | sed 's/\/classes\//\/classes /g' | awk '{print "--classfiles", $1, "\\"}' | sort |uniq >> jacoco-report.sh
find . | grep "src/main/java" | sed 's/\/java\//\/java /g' | awk '{print "--sourcefiles", $1, "\\"}' | sort | uniq >> jacoco-report.sh
echo "--xml jacoco-coverage.xml" >> jacoco-report.sh
-
- name: Start CloudStack Management Server with Simulator
run: |
export MAVEN_OPTS="-Xmx4096m -XX:MaxMetaspaceSize=800m -Djava.security.egd=file:/dev/urandom -javaagent:jacoco/lib/jacocoagent.jar=address=*,port=36320,output=tcpserver --add-opens=java.base/java.lang=ALL-UNNAMED --add-exports=java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED"
@@ -292,7 +328,6 @@ jobs:
set -e
echo -e "\nStarting Advanced Zone DataCenter deployment"
python3 tools/marvin/marvin/deployDataCenter.py -i setup/dev/advdualzone.cfg 2>&1 || true
-
- name: Run Integration Tests with Simulator
run: |
mkdir -p integration-test-results/smoke/misc
@@ -312,13 +347,12 @@ jobs:
bash jacoco-report.sh
mvn -Dsimulator -pl client jetty:stop 2>&1
find /tmp//MarvinLogs -type f -exec echo -e "Printing marvin logs {} :\n" \; -exec cat {} \;
-
- name: Integration Tests Result
run: |
echo -e "Simulator CI Test Results: (only failures listed)\n"
python3 ./tools/marvin/xunit-reader.py integration-test-results/
- - uses: codecov/codecov-action@v4
+ - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
files: jacoco-coverage.xml
fail_ci_if_error: true
diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml
deleted file mode 100644
index c77783746ca1..000000000000
--- a/.github/workflows/codecov.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-# 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.
-
-name: Coverage Check
-
-on: [pull_request, push]
-
-permissions:
- contents: read
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
-
-jobs:
- build:
- if: github.repository == 'apache/cloudstack'
- name: codecov
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Set up JDK 17
- uses: actions/setup-java@v4
- with:
- distribution: 'temurin'
- java-version: '17'
- cache: 'maven'
-
- - name: Build CloudStack with Quality Checks
- run: |
- git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss
- cd nonoss && bash -x install-non-oss.sh && cd ..
- mvn -P quality -Dsimulator -Dnoredist clean install -T$(nproc)
-
- - uses: codecov/codecov-action@v4
- with:
- files: ./client/target/site/jacoco-aggregate/jacoco.xml
- fail_ci_if_error: true
- flags: unittests
- verbose: true
- name: codecov
- token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000000..f6bc65ad4b5b
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,50 @@
+# 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.
+
+name: CodeQL Analysis
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+permissions:
+ actions: read
+ contents: read
+ security-events: write
+jobs:
+ codeql:
+ name: CodeQL
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ["actions"]
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
+ with:
+ languages: ${{ matrix.language }}
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
+ with:
+ category: "Security"
diff --git a/.github/workflows/daily-issue-triage.lock.yml b/.github/workflows/daily-issue-triage.lock.yml
new file mode 100644
index 000000000000..bd07aeefd811
--- /dev/null
+++ b/.github/workflows/daily-issue-triage.lock.yml
@@ -0,0 +1,1400 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"919fb17c7928e5e96d9c0a2854670a42f9c5f6cfc2059b46009bb3c23640d0ca","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"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.76.1). DO NOT EDIT.
+#
+# To update this file, edit githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9 and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Scheduled daily triage that processes untriaged CloudStack issues in batches.
+# Detects duplicates, filters spam, and assigns CloudStack-specific labels
+# (type:*, component:*, Severity:*, status:*), then posts a structured triage report.
+#
+# Source: githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca
+# - ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f
+# - ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4
+# - node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+
+name: "Daily Issue Triage"
+on:
+ schedule:
+ - cron: "26 13 * * 1-5"
+ # Friendly format: daily around 14:00 on weekdays (scattered)
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "Daily Issue Triage"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AGENT_VERSION: "1.0.52"
+ GH_AW_INFO_CLI_VERSION: "v0.76.1"
+ GH_AW_INFO_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_INFO_FRONTMATTER_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_COMPILED_STRICT: "true"
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ env:
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ - name: Checkout .github and .agents folders
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ .antigravity
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "daily-issue-triage.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.76.1"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_7c51e8f15cc7af75_EOF'
+
+ GH_AW_PROMPT_7c51e8f15cc7af75_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_7c51e8f15cc7af75_EOF'
+
+ Tools: add_comment(max:10), add_labels(max:10), missing_tool, missing_data, noop
+
+ GH_AW_PROMPT_7c51e8f15cc7af75_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_7c51e8f15cc7af75_EOF'
+
+ The following GitHub context information is available for this workflow:
+ {{#if github.actor}}
+ - **actor**: __GH_AW_GITHUB_ACTOR__
+ {{/if}}
+ {{#if github.repository}}
+ - **repository**: __GH_AW_GITHUB_REPOSITORY__
+ {{/if}}
+ {{#if github.workspace}}
+ - **workspace**: __GH_AW_GITHUB_WORKSPACE__
+ {{/if}}
+ {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
+ - **issue-number**: #__GH_AW_EXPR_802A9F6A__
+ {{/if}}
+ {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
+ - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
+ {{/if}}
+ {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
+ - **pull-request-number**: #__GH_AW_EXPR_463A214A__
+ {{/if}}
+ {{#if github.event.comment.id || github.aw.context.comment_id}}
+ - **comment-id**: __GH_AW_EXPR_FF1D34CE__
+ {{/if}}
+ {{#if github.run_id}}
+ - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
+ {{/if}}
+
+
+ GH_AW_PROMPT_7c51e8f15cc7af75_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_7c51e8f15cc7af75_EOF'
+
+ {{#runtime-import .github/workflows/daily-issue-triage.md}}
+ GH_AW_PROMPT_7c51e8f15cc7af75_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
+ GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
+ GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
+ GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
+ GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
+ GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
+ GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/aw-prompts/prompt-template.txt
+ /tmp/gh-aw/aw-prompts/prompt-import-tree.json
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ /tmp/gh-aw/.github/agents
+ /tmp/gh-aw/.github/skills
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions: read-all
+ concurrency:
+ group: "gh-aw-copilot-${{ github.workflow }}"
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: dailyissuetriage
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - name: Checkout repository
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ - name: Create gh-aw temp directory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
+ - name: Configure gh CLI for GitHub Enterprise
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
+ env:
+ GH_TOKEN: ${{ github.token }}
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Checkout PR branch
+ id: checkout-pr
+ if: |
+ github.event.pull_request || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Parse integrity filter lists
+ id: parse-guard-vars
+ env:
+ GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }}
+ GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }}
+ GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh"
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Restore inline sub-agents from activation artifact
+ env:
+ GH_AW_SUB_AGENT_DIR: ".github/agents"
+ GH_AW_SUB_AGENT_EXT: ".agent.md"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
+ - name: Restore inline skills from activation artifact
+ env:
+ GH_AW_SKILL_DIR: ".github/skills"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+ - name: Generate Safe Outputs Config
+ run: |
+ 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_09fd9551c3cd7278_EOF'
+ {"add_comment":{"max":10,"target":"*"},"add_labels":{"max":10,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_09fd9551c3cd7278_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "add_comment": " CONSTRAINTS: Maximum 10 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading.",
+ "add_labels": " CONSTRAINTS: Maximum 10 label(s) can be added. Target: *."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "add_comment": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "item_number": {
+ "issueOrPRNumber": true
+ },
+ "reply_to_id": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ }
+ }
+ },
+ "add_labels": {
+ "defaultMax": 5,
+ "fields": {
+ "item_number": {
+ "issueNumberOrTemporaryId": true
+ },
+ "labels": {
+ "required": true,
+ "type": "array",
+ "itemType": "string",
+ "itemSanitize": true,
+ "itemMaxLength": 128
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ run: |
+ set -eo pipefail
+ mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ case "${DOCKER_HOST:-}" in
+ unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
+ /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
+ * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
+ esac
+ DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.19'
+
+ 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_37cac1d5ee0c175c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v1.0.4",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "issues,labels"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }},
+ "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }},
+ "min-integrity": "none",
+ "repos": "all",
+ "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }}
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_37cac1d5ee0c175c_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 60
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55,squid=sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca,agent=sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731,api-proxy=sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.76.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect agent errors
+ if: always()
+ id: detect-agent-errors
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - name: Stop MCP Gateway
+ if: always()
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs');
+ await main();
+ env:
+ GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
+ SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ await main();
+ - name: Print firewall logs
+ if: always()
+ continue-on-error: true
+ env:
+ AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
+ run: |
+ # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/proxy-logs/
+ !/tmp/gh-aw/proxy-logs/proxy-tls/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ pull-requests: write
+ concurrency:
+ group: "gh-aw-conclusion-daily-issue-triage"
+ cancel-in-progress: false
+ queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "daily-issue-triage"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
+ GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "60"
+ GH_AW_MAX_EFFECTIVE_TOKENS: "25000000"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP Config for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ WORKFLOW_NAME: "Daily Issue Triage"
+ WORKFLOW_DESCRIPTION: "Scheduled daily triage that processes untriaged CloudStack issues in batches.\nDetects duplicates, filters spam, and assigns CloudStack-specific labels\n(type:*, component:*, Severity:*, status:*), then posts a structured triage report."
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.76.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ try {
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError && !detectionExecutionFailed) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ pull-requests: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-issue-triage"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.52"
+ GH_AW_WORKFLOW_ID: "daily-issue-triage"
+ GH_AW_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/d7c1dc4b72b00607a67caaffdcc216cb64379cf9/workflows/daily-issue-triage.md"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
+ comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Daily Issue Triage"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-issue-triage.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Configure GH_HOST for enterprise compatibility
+ id: ghes-host-config
+ shell: bash
+ run: |
+ # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
+ # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
+ GH_HOST="${GITHUB_SERVER_URL#https://}"
+ GH_HOST="${GH_HOST#http://}"
+ echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
diff --git a/.github/workflows/daily-issue-triage.md b/.github/workflows/daily-issue-triage.md
new file mode 100644
index 000000000000..719dca1f3c63
--- /dev/null
+++ b/.github/workflows/daily-issue-triage.md
@@ -0,0 +1,171 @@
+---
+description: |
+ Scheduled daily triage that processes untriaged CloudStack issues in batches.
+ Detects duplicates, filters spam, and assigns CloudStack-specific labels
+ (type:*, component:*, Severity:*, status:*), then posts a structured triage report.
+
+name: Daily Issue Triage
+
+on:
+ schedule: daily around 14:00 on weekdays
+ workflow_dispatch:
+
+permissions: read-all
+
+network: defaults
+
+safe-outputs:
+ add-labels:
+ target: "*"
+ max: 10
+ add-comment:
+ target: "*"
+ max: 10
+
+tools:
+ web-fetch:
+ github:
+ toolsets: [issues, labels]
+ min-integrity: none
+
+source: githubnext/agentics/workflows/daily-issue-triage.md@d7c1dc4b72b00607a67caaffdcc216cb64379cf9
+timeout-minutes: 60
+---
+
+# Daily Issue Triage
+
+
+
+You are a batch triage assistant for GitHub issues in **${{ github.repository }}** (Apache CloudStack). Your task is to find untriaged issues and triage them one by one. Your triage comments are written for maintainers reviewing the triage, not for the issue author.
+
+Do not make assumptions beyond what the issue content supports. Do not invent missing context.
+
+## Step 1: Find untriaged issues
+
+Use the `search_issues` tool to find open issues that need triage. An issue is considered untriaged if it has **no labels applied**.
+
+Query: `repo:${{ github.repository }} is:issue is:open no:label`
+
+Paginate through all results to find untriaged issues. Do not stop at the first page.
+
+From the results, filter out:
+- Issues that already have a triage comment (look for "🎯 Triage report" in comments). **Never retriage an issue that has already been triaged.**
+- Issues created by bots (unless they look like real user issues).
+- Issues that have any labels already applied (even if they weren't applied by this workflow).
+
+Process the **oldest untriaged issues first**. Note: this workflow is capped at 10 label-sets and 10 comments per run, so the backlog will drain over several daily runs — that is intentional.
+
+## Step 2: Fetch labels (once)
+
+Before triaging any issues, fetch the list of labels available in this repository using the `list_labels` tool. Use this live list for all issues in the batch — only apply labels that actually exist in the repository.
+
+CloudStack uses a prefixed label taxonomy. Choose from these families:
+
+- **Type** (pick the single best one): `type:bug`, `type:new-feature`, `type:enhancement`, `type:improvement`, `type:regression`, `type:security`, `type:question`, `type:config`, `type:cleanup`
+- **Component** (apply when clearly identifiable; more than one is allowed): e.g. `component:kvm`, `component:vmware`, `component:XenServer`, `component:api`, `component:UI`, `component:networking`, `component:virtual-router`, `component:management-server`, `component:primary-storage`, `component:secondary-storage`, `component:kubernetes`, `component:database`, and others — use the full list returned by `list_labels`.
+- **Severity** (bugs only, when assessable): `Severity:BLOCKER`, `Severity:Critical`, `Severity:Major`, `Severity:Minor`, `Severity:Trivial`
+- **Duplicate / invalid**: `status:duplicate`, `status:invalid`
+- **Help wanted / newcomer-friendly**: `status:Help-wanted`
+
+## Step 3: Triage each issue
+
+For each untriaged issue, perform the following steps:
+
+### 3a: Gather context
+
+1. Retrieve the full issue content using the `get_issue` tool.
+2. Fetch any comments on the issue using the `get_issue_comments` tool.
+3. Search for similar issues using the `search_issues` tool.
+
+### 3b: Spam and quality check
+
+**Spam and invalid issues:** If the issue is obviously spam, bot-generated, gibberish, or a test issue:
+- Apply the `status:invalid` label.
+- **Do not close the issue** — closing is a human decision. Note in the report that it looks like spam/invalid so a maintainer can act.
+- Move to the next issue.
+
+**Incomplete issues:** If the issue lacks enough detail for meaningful triage, add a comment that politely asks the author to provide the missing information:
+- For bugs: steps to reproduce, expected vs actual behavior, logs/errors, environment details (CloudStack version, hypervisor, etc.).
+- For other issue types: equivalent details that would make the report actionable.
+- Apply a `type:question` label if appropriate.
+- Be specific about what is missing and why it is needed.
+- Move to the next issue.
+
+### 3c: Select labels
+
+- Be cautious with labels; they can trigger automation.
+- Choose a single `type:*` label that best reflects the issue's nature.
+- Add `component:*` label(s) when the affected area is clear from the content.
+- Add a `Severity:*` label for bugs when severity can be reasonably assessed.
+- Do not apply labels that do not exist in the repository.
+- It is better to under-label than to speculatively add labels.
+
+### 3d: Detect duplicates and related issues
+
+- Review the similar issues found in Step 3a.
+- Classify matches as:
+ - **Duplicate** (high confidence): the issue describes the same problem as an existing open issue. Include up to 3.
+ - **Related**: similar domain or adjacent problem, but not a duplicate. Include up to 3.
+- If a high-confidence duplicate is found, apply the `status:duplicate` label.
+- If no similar issues are found, state that explicitly in your report.
+
+### 3e: Assess coding agent suitability
+
+Assess whether the issue is suitable for automated coding agent assignment:
+- **Suitable**: clear requirements, sufficient context, well-defined success criteria, self-contained scope.
+- **Needs more info**: potentially suitable but missing details needed to start.
+- **Not suitable**: requires investigation, design decisions, extensive coordination, or policy/architectural choices.
+
+### 3f: Additional analysis
+
+- Search the web for relevant documentation, error messages, or known solutions if applicable.
+- Write notes, debugging strategies, and/or reproduction steps relevant to the issue.
+- Suggest resources or links that might help resolve the issue.
+
+### 3g: Apply results and post comment
+
+Apply all triage results for this issue:
+- Use `update_issue` to apply the chosen labels.
+- Add an issue comment with the triage report using the format below.
+
+Then move to the next issue.
+
+## Processing order
+
+1. Fetch available labels (Step 2, once at the start).
+2. Find untriaged issues (Step 1).
+3. For each issue (oldest first), run Step 3 (gather, check, label, detect duplicates, comment).
+
+## Comment format
+
+Use this structure for each triage comment. Use collapsed sections to keep it tidy.
+
+```markdown
+## 🎯 Triage report
+
+{2-3 sentence summary to help a maintainer quickly grasp the issue.}
+
+### 📊 Assessment
+
+| Dimension | Value | Reasoning |
+|---|---|---|
+| **Type** | [type:* label or "none"] | [brief] |
+| **Component** | [component:* label(s) or "none"] | [brief] |
+| **Severity** | [Severity:* label or "n/a"] | [brief] |
+| **Labels** | [all labels applied or "none"] | [brief] |
+| **Coding agent** | [Suitable / Needs more info / Not suitable] | [brief] |
+
+### 🔗 Similar issues
+
+- issue-url (duplicate/related) — [brief explanation]
+
+💡 Notes and suggestions
+
+{Debugging strategies, reproduction steps, resource links, sub-task checklists, nudges for the team.}
+
+
+```
+
+If no similar issues were found, omit the "Similar issues" section. If there are no notes to add, omit the collapsed section.
+
+**Important**: Never close issues. Only apply labels and post comments.
diff --git a/.github/workflows/daily-repo-status.lock.yml b/.github/workflows/daily-repo-status.lock.yml
new file mode 100644
index 000000000000..0992d3b67de0
--- /dev/null
+++ b/.github/workflows/daily-repo-status.lock.yml
@@ -0,0 +1,1394 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"bcecce6f1d9f8df2b3eca9eb7bb1fdbac13c396c240a2dc802a96546f435b969","compiler_version":"v0.76.1","strict":true,"agent_id":"copilot","agent_model":"claude-haiku-4.5"}
+# 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"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.76.1). DO NOT EDIT.
+#
+# To update this file, edit githubnext/agentics/workflows/repo-status.md@main and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# This workflow creates daily repo status reports. It gathers recent repository
+# activity (issues, PRs, discussions, releases, code changes) and generates
+# engaging GitHub issues with productivity insights, community highlights,
+# and project recommendations.
+#
+# Source: githubnext/agentics/workflows/repo-status.md@main
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca
+# - ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f
+# - ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4
+# - node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+
+name: "Repo Status"
+on:
+ schedule:
+ - cron: "11 19 * * *"
+ # Friendly format: daily (scattered)
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "Repo Status"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Repo Status"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: "claude-haiku-4.5"
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AGENT_VERSION: "1.0.52"
+ GH_AW_INFO_CLI_VERSION: "v0.76.1"
+ GH_AW_INFO_WORKFLOW_NAME: "Repo Status"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_INFO_FRONTMATTER_SOURCE: "githubnext/agentics/workflows/repo-status.md@main"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_COMPILED_STRICT: "true"
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ env:
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ - name: Checkout .github and .agents folders
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ .antigravity
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "daily-repo-status.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.76.1"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_eeb322738661ed58_EOF'
+
+ GH_AW_PROMPT_eeb322738661ed58_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_eeb322738661ed58_EOF'
+
+ Tools: create_issue, missing_tool, missing_data, noop
+
+ GH_AW_PROMPT_eeb322738661ed58_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_eeb322738661ed58_EOF'
+
+ The following GitHub context information is available for this workflow:
+ {{#if github.actor}}
+ - **actor**: __GH_AW_GITHUB_ACTOR__
+ {{/if}}
+ {{#if github.repository}}
+ - **repository**: __GH_AW_GITHUB_REPOSITORY__
+ {{/if}}
+ {{#if github.workspace}}
+ - **workspace**: __GH_AW_GITHUB_WORKSPACE__
+ {{/if}}
+ {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
+ - **issue-number**: #__GH_AW_EXPR_802A9F6A__
+ {{/if}}
+ {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
+ - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
+ {{/if}}
+ {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
+ - **pull-request-number**: #__GH_AW_EXPR_463A214A__
+ {{/if}}
+ {{#if github.event.comment.id || github.aw.context.comment_id}}
+ - **comment-id**: __GH_AW_EXPR_FF1D34CE__
+ {{/if}}
+ {{#if github.run_id}}
+ - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
+ {{/if}}
+
+
+ GH_AW_PROMPT_eeb322738661ed58_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_eeb322738661ed58_EOF'
+
+ {{#runtime-import .github/workflows/daily-repo-status.md}}
+ GH_AW_PROMPT_eeb322738661ed58_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
+ GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
+ GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
+ GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
+ GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
+ GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
+ GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/aw-prompts/prompt-template.txt
+ /tmp/gh-aw/aw-prompts/prompt-import-tree.json
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ /tmp/gh-aw/.github/agents
+ /tmp/gh-aw/.github/skills
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ issues: read
+ pull-requests: read
+ concurrency:
+ group: "gh-aw-copilot-${{ github.workflow }}"
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: dailyrepostatus
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Repo Status"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - name: Checkout repository
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ - name: Create gh-aw temp directory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
+ - name: Configure gh CLI for GitHub Enterprise
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
+ env:
+ GH_TOKEN: ${{ github.token }}
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Checkout PR branch
+ id: checkout-pr
+ if: |
+ github.event.pull_request || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Parse integrity filter lists
+ id: parse-guard-vars
+ env:
+ GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }}
+ GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }}
+ GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }}
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh"
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Restore inline sub-agents from activation artifact
+ env:
+ GH_AW_SUB_AGENT_DIR: ".github/agents"
+ GH_AW_SUB_AGENT_EXT: ".agent.md"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
+ - name: Restore inline skills from activation artifact
+ env:
+ GH_AW_SKILL_DIR: ".github/skills"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca ghcr.io/github/gh-aw-mcpg:v0.3.19@sha256:a6c890d7c24d7190c9ef97b9c954cc4cffaae6b01c371ced1f959f1370b1f68f ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14
+ - name: Generate Safe Outputs Config
+ run: |
+ 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_51571b44da85874d_EOF'
+ {"create_issue":{"close_older_issues":true,"labels":["report","daily-status"],"max":1,"title_prefix":"[repo-status] "},"create_report_incomplete_issue":{},"mentions":{"enabled":false},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_51571b44da85874d_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[repo-status] \". Labels [\"report\" \"daily-status\"] will be automatically added."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_issue": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "fields": {
+ "type": "array"
+ },
+ "labels": {
+ "type": "array",
+ "itemType": "string",
+ "itemSanitize": true,
+ "itemMaxLength": 128
+ },
+ "parent": {
+ "issueOrPRNumber": true
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "temporary_id": {
+ "type": "string"
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ run: |
+ set -eo pipefail
+ mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ case "${DOCKER_HOST:-}" in
+ unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
+ /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
+ * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
+ esac
+ DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.19'
+
+ 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_4b3a7789a6eea081_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v1.0.4",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }},
+ "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }},
+ "min-integrity": "none",
+ "repos": "all",
+ "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }}
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_4b3a7789a6eea081_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55,squid=sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca,agent=sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731,api-proxy=sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: claude-haiku-4.5
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.76.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect agent errors
+ if: always()
+ id: detect-agent-errors
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - name: Stop MCP Gateway
+ if: always()
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs');
+ await main();
+ env:
+ GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
+ SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
+ GH_AW_ALLOWED_GITHUB_REFS: ""
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ await main();
+ - name: Print firewall logs
+ if: always()
+ continue-on-error: true
+ env:
+ AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
+ run: |
+ # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/proxy-logs/
+ !/tmp/gh-aw/proxy-logs/proxy-tls/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ issues: write
+ concurrency:
+ group: "gh-aw-conclusion-daily-repo-status"
+ cancel-in-progress: false
+ queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Repo Status"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Repo Status"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Repo Status"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Repo Status"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Repo Status"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Repo Status"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "daily-repo-status"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
+ GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "20"
+ GH_AW_MAX_EFFECTIVE_TOKENS: "25000000"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Repo Status"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55@sha256:138c363411decc9a61a5af9b95e8d64c76648b00add0ba06fc7ba786f0e72731 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55@sha256:4142b873b678cd3279b98dcbe464857d56ea2f2348719b00379cdf35dd843ff3 ghcr.io/github/gh-aw-firewall/squid:0.25.55@sha256:74084b704d8d3664a363655986664d70bd9cdb4830532d0b35cd784d867aabca
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP Config for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ WORKFLOW_NAME: "Repo Status"
+ WORKFLOW_DESCRIPTION: "This workflow creates daily repo status reports. It gathers recent repository\nactivity (issues, PRs, discussions, releases, code changes) and generates\nengaging GitHub issues with productivity insights, community highlights,\nand project recommendations."
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: claude-haiku-4.5
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.76.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ try {
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError && !detectionExecutionFailed) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ issues: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/daily-repo-status"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: "claude-haiku-4.5"
+ GH_AW_ENGINE_VERSION: "1.0.52"
+ GH_AW_WORKFLOW_ID: "daily-repo-status"
+ GH_AW_WORKFLOW_NAME: "Repo Status"
+ GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-status.md@main"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/main/workflows/repo-status.md"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
+ created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Repo Status"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-repo-status.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_BODY_MODIFIED: "false"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Configure GH_HOST for enterprise compatibility
+ id: ghes-host-config
+ shell: bash
+ run: |
+ # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
+ # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
+ GH_HOST="${GITHUB_SERVER_URL#https://}"
+ GH_HOST="${GH_HOST#http://}"
+ echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"labels\":[\"report\",\"daily-status\"],\"max\":1,\"title_prefix\":\"[repo-status] \"},\"create_report_incomplete_issue\":{},\"mentions\":{\"enabled\":false},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
diff --git a/.github/workflows/daily-repo-status.md b/.github/workflows/daily-repo-status.md
new file mode 100644
index 000000000000..49b553940b8b
--- /dev/null
+++ b/.github/workflows/daily-repo-status.md
@@ -0,0 +1,62 @@
+---
+description: |
+ This workflow creates daily repo status reports. It gathers recent repository
+ activity (issues, PRs, discussions, releases, code changes) and generates
+ engaging GitHub issues with productivity insights, community highlights,
+ and project recommendations.
+
+on:
+ schedule: daily
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ issues: read
+ pull-requests: read
+
+network: defaults
+
+engine:
+ id: copilot
+ model: claude-haiku-4.5
+
+tools:
+ github:
+ # If in a public repo, setting `lockdown: false` allows
+ # reading issues, pull requests and comments from 3rd-parties
+ # If in a private repo this has no particular effect.
+ lockdown: false
+ min-integrity: none # This workflow is allowed to examine and comment on any issues
+
+safe-outputs:
+ mentions: false
+ allowed-github-references: []
+ create-issue:
+ title-prefix: "[repo-status] "
+ labels: [report, daily-status]
+ close-older-issues: true
+source: githubnext/agentics/workflows/repo-status.md@main
+---
+
+# Repo Status
+
+Create an upbeat daily status report for the repo as a GitHub issue.
+
+## What to include
+
+- Recent repository activity (issues, PRs, discussions, releases, code changes)
+- Progress tracking, goal reminders and highlights
+- Project status and recommendations
+- Actionable next steps for maintainers
+
+## Style
+
+- Be positive, encouraging, and helpful 🌟
+- Use emojis moderately for engagement
+- Keep it concise - adjust length based on actual activity
+
+## Process
+
+1. Gather recent activity from the repository
+2. Study the repository, its issues and its pull requests
+3. Create a new GitHub issue with your findings and insights
diff --git a/.github/workflows/docker-cloudstack-simulator.yml b/.github/workflows/docker-cloudstack-simulator.yml
index 21a09d04e0b3..ac778a48af54 100644
--- a/.github/workflows/docker-cloudstack-simulator.yml
+++ b/.github/workflows/docker-cloudstack-simulator.yml
@@ -35,10 +35,10 @@ concurrency:
jobs:
build:
if: github.repository == 'apache/cloudstack'
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: Login to Docker Registry
- uses: docker/login-action@v2
+ uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKERHUB_USER }}
@@ -47,7 +47,9 @@ jobs:
- name: Set Docker repository name
run: echo "DOCKER_REPOSITORY=apache" >> $GITHUB_ENV
- - uses: actions/checkout@v4
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
- name: Set ACS version
run: echo "ACS_VERSION=$(grep '' pom.xml | head -2 | tail -1 | cut -d'>' -f2 |cut -d'<' -f1)" >> $GITHUB_ENV
diff --git a/.github/workflows/license-templates/LICENSE.txt b/.github/workflows/license-templates/LICENSE.txt
new file mode 100644
index 000000000000..60b675e31016
--- /dev/null
+++ b/.github/workflows/license-templates/LICENSE.txt
@@ -0,0 +1,16 @@
+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.
diff --git a/.github/workflows/main-sonar-check.yml b/.github/workflows/main-sonar-check.yml
index 8248e48022a3..f48409fbc0c3 100644
--- a/.github/workflows/main-sonar-check.yml
+++ b/.github/workflows/main-sonar-check.yml
@@ -15,54 +15,53 @@
# specific language governing permissions and limitations
# under the License.
-name: Main Branch Sonar Quality Check
-
+name: Sonar Quality Check (Main)
+permissions:
+ contents: read
on:
push:
branches:
- main
-
-permissions:
- contents: read # to fetch code (actions/checkout)
- pull-requests: write # for sonar to comment on pull-request
-
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
build:
if: github.repository == 'apache/cloudstack'
- name: Main Sonar JaCoCo Build
- runs-on: ubuntu-22.04
+ name: Sonar JaCoCo Coverage
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
-
- - name: Set up JDK17
- uses: actions/setup-java@v4
+ persist-credentials: false
+ - name: Setup Environment
+ uses: ./.github/actions/setup-env
with:
- distribution: 'temurin'
- java-version: '17'
- cache: 'maven'
-
+ install-python: 'true'
+ install-apt-deps: 'true'
- name: Cache SonarCloud packages
- uses: actions/cache@v4
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
-
- - name: Cache local Maven repository
- uses: actions/cache@v4
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-m2
-
- - name: Run Tests with Coverage
+ - name: Install Non-OSS
+ uses: ./.github/actions/install-nonoss
+ - name: Run Build and Tests with Coverage
+ run: mvn -B -T$(nproc) -P developer,systemvm,quality -Dsimulator -Dnoredist clean install
+ - name: Upload to SonarQube
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- run: |
- git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss
- cd nonoss && bash -x install-non-oss.sh && cd ..
- mvn -T$(nproc) -P quality -Dsimulator -Dnoredist clean install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack
+ run: mvn -B -P quality org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack -Dsonar.branch.name=${{ github.ref_name }}
+ - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
+ with:
+ files: ./client/target/site/jacoco-aggregate/jacoco.xml
+ fail_ci_if_error: true
+ flags: unittests
+ verbose: true
+ name: codecov
+ token: ${{ secrets.CODECOV_TOKEN }}
+ - name: Compute Coverage Grade
+ run: bash scripts/coverage-grade.sh client/target/site/jacoco-aggregate/jacoco.xml
diff --git a/.github/workflows/merge-conflict-checker.yml b/.github/workflows/merge-conflict-checker.yml
index 860e1c1b5614..badf8c4b4c5b 100644
--- a/.github/workflows/merge-conflict-checker.yml
+++ b/.github/workflows/merge-conflict-checker.yml
@@ -17,28 +17,26 @@
name: "PR Merge Conflict Check"
on:
- push:
- pull_request_target:
- types: [synchronize]
+ schedule:
+ - cron: '*/10 * * * *'
+ workflow_dispatch:
-permissions: # added using https://github.com/step-security/secure-workflows
- contents: read
+permissions: {}
concurrency:
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
+ group: "gh-aw-${{ github.workflow }}"
jobs:
triage:
permissions:
- pull-requests: write # for eps1lon/actions-label-merge-conflict to label PRs
- runs-on: ubuntu-22.04
+ pull-requests: write # for eps1lon/actions-label-merge-conflict to label PRs
+ runs-on: ubuntu-24.04
steps:
- - name: Conflict Check
- uses: eps1lon/actions-label-merge-conflict@v2.0.0
- with:
- repoToken: "${{ secrets.GITHUB_TOKEN }}"
- dirtyLabel: "status:has-conflicts"
- removeOnDirtyLabel: "status:ready-for-review"
- continueOnMissingPermissions: true
- commentOnDirty: "This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch."
+ - name: Conflict Check
+ uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0
+ with:
+ repoToken: "${{ secrets.GITHUB_TOKEN }}"
+ dirtyLabel: "status:has-conflicts"
+ removeOnDirtyLabel: "status:ready-for-review"
+ continueOnMissingPermissions: true
+ commentOnDirty: "This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch."
diff --git a/.github/workflows/linter.yml b/.github/workflows/pre-commit.yml
similarity index 65%
rename from .github/workflows/linter.yml
rename to .github/workflows/pre-commit.yml
index b6c814a36f4c..4de995479c59 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/pre-commit.yml
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-name: Lint
+name: pre-commit
on: [pull_request]
@@ -29,19 +29,27 @@ concurrency:
jobs:
pre-commit:
name: Run pre-commit
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: Check Out
- uses: actions/checkout@v4
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
+ - name: Set up Python
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+ with:
+ python-version: '3.11'
+ cache: 'pip'
- name: Install
- run: |
- python -m pip install --upgrade pip
- pip install pre-commit
+ run: pip install pre-commit
- name: Set PY
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
- - uses: actions/cache@v4
+ - name: Cache pre-commit environments
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit
- run: pre-commit run --all-files
+ run: pre-commit run --color=always --all-files
+ - name: Run manual pre-commit hooks
+ run: pre-commit run --color=always --all-files --hook-stage manual
diff --git a/.github/workflows/rat.yml b/.github/workflows/rat.yml
index 52ce343841ba..b75ff42b9122 100644
--- a/.github/workflows/rat.yml
+++ b/.github/workflows/rat.yml
@@ -16,32 +16,31 @@
# under the License.
name: License Check
-
-on: [push, pull_request]
-
+on:
+ push:
+ branches:
+ - main
+ - 4.22
+ - 4.20
+ pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
-
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
permissions:
contents: read
-
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v4
- - name: Set up JDK 17
- uses: actions/setup-java@v4
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
- java-version: '17'
- distribution: 'adopt'
- architecture: x64
- cache: maven
+ persist-credentials: false
+ - name: Setup Environment
+ uses: ./.github/actions/setup-env
+ - name: Install Non-OSS
+ uses: ./.github/actions/install-nonoss
- name: RAT licence checks
run: |
- git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss && cd nonoss && bash -x install-non-oss.sh && cd ..
- rm -fr nonoss
mvn -P developer,systemvm -Dsimulator -Dnoredist -pl . org.apache.rat:apache-rat-plugin:0.12:check
- name: Rat Report
if: always()
diff --git a/.github/workflows/sonar-check.yml b/.github/workflows/sonar-check.yml
index c36bceb2b901..fbb3cb9f540d 100644
--- a/.github/workflows/sonar-check.yml
+++ b/.github/workflows/sonar-check.yml
@@ -16,58 +16,100 @@
# under the License.
name: Sonar Quality Check
-
-on: [pull_request]
-
permissions:
- contents: read # to fetch code (actions/checkout)
- pull-requests: write # for sonar to comment on pull-request
-
+ contents: read
+ pull-requests: write
+on:
+ pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: true
-
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
build:
- if: github.repository == 'apache/cloudstack' && github.event.pull_request.head.repo.full_name == github.repository
name: Sonar JaCoCo Coverage
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
- ref: "refs/pull/${{ github.event.number }}/merge"
fetch-depth: 0
-
- - name: Set up JDK17
- uses: actions/setup-java@v4
+ persist-credentials: false
+ - name: Setup Environment
+ uses: ./.github/actions/setup-env
with:
- distribution: 'temurin'
- java-version: '17'
- cache: 'maven'
-
+ install-python: 'true'
+ install-apt-deps: 'true'
- name: Cache SonarCloud packages
- uses: actions/cache@v4
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
-
- - name: Cache local Maven repository
- uses: actions/cache@v4
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-m2
-
+ - name: Install Non-OSS
+ uses: ./.github/actions/install-nonoss
- name: Run Build and Tests with Coverage
- id: coverage
+ run: mvn -B -T$(nproc) -P developer,systemvm,quality -Dsimulator -Dnoredist clean install
+ - name: Upload to SonarQube
+ if: github.repository == 'apache/cloudstack' && github.event.pull_request.head.repo.full_name == github.repository
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
PR_ID: ${{ github.event.pull_request.number }}
HEADREF: ${{ github.event.pull_request.head.ref }}
run: |
- git clone https://github.com/shapeblue/cloudstack-nonoss.git nonoss
- cd nonoss && bash -x install-non-oss.sh && cd ..
- mvn -T$(nproc) -P quality -Dsimulator -Dnoredist clean install org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack -Dsonar.pullrequest.key="$PR_ID" -Dsonar.pullrequest.branch="$HEADREF" -Dsonar.pullrequest.github.repository=apache/cloudstack -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.summary_comment=true
+ mvn -B -P quality org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=apache_cloudstack -Dsonar.pullrequest.key="$PR_ID" -Dsonar.pullrequest.branch="$HEADREF" -Dsonar.pullrequest.github.repository=apache/cloudstack -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.summary_comment=true
+ - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
+ with:
+ files: ./client/target/site/jacoco-aggregate/jacoco.xml
+ fail_ci_if_error: true
+ flags: unittests
+ verbose: true
+ name: codecov
+ token: ${{ secrets.CODECOV_TOKEN }}
+ - name: Compute Coverage Grade
+ id: grade
+ run: bash scripts/coverage-grade.sh client/target/site/jacoco-aggregate/jacoco.xml
+ - name: Post Coverage Grade Comment on PR
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const grade = '${{ steps.grade.outputs.coverage_grade }}';
+ const label = '${{ steps.grade.outputs.coverage_grade_label }}';
+ const linePct = '${{ steps.grade.outputs.line_coverage }}';
+ const branchPct = '${{ steps.grade.outputs.branch_coverage }}';
+ const emojiMap = { A: '🟢', B: '🟡', C: '🟠', D: '🔴', F: '⛔' };
+ const emoji = emojiMap[grade] ?? '❓';
+ const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
+
+ const branchRow = branchPct !== 'N/A'
+ ? `| Branch coverage | **${branchPct}%** |`
+ : '';
+
+ const body = [
+ `## ${emoji} Test Coverage Grade: \`${grade}\` — ${label}`,
+ '',
+ '| Metric | Value |',
+ '|--------|-------|',
+ `| Line coverage | **${linePct}%** |`,
+ branchRow,
+ '',
+ '### Grade Scale',
+ '| Grade | Line Coverage | Meaning |',
+ '|-------|--------------|---------|',
+ '| 🟢 A | ≥ 80% | Excellent - this code sleeps well at night 😴 |',
+ '| 🟡 B | 60-79% | Good - almost there, don\'t stop now 😉 |',
+ '| 🟠 C | 40-59% | Acceptable - your code is wearing a seatbelt, but no airbags 😬 |',
+ '| 🔴 D | 20-39% | Marginal - boldly shipping where no test has gone before 🖖 |',
+ '| ⛔ F | < 20% | Failing - tests? what tests? 🔥 |',
+ '',
+ '> Branch coverage is shown as a secondary signal. Grade is determined by **line coverage**.',
+ `> [View full Actions run](${runUrl})`,
+ ].filter(l => l !== undefined).join('\n');
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body: body,
+ });
+ console.log('Posted coverage grade comment');
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000000..c0da5f98cc8e
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,49 @@
+# 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.
+
+name: 'Close stale issues and PRs'
+on:
+ schedule:
+ - cron: '30 1 * * *'
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ permissions:
+ actions: write
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
+ with:
+ stale-issue-message: 'This issue is stale because it has been open for 120 days with no activity. It may be removed by administrators of this project at any time. Remove the stale label or comment to request for removal of it to prevent this.'
+ stale-pr-message: 'This PR is stale because it has been open for 120 days with no activity. It may be removed by administrators of this project at any time. Remove the stale label or comment to request for removal of it to prevent this.'
+ close-issue-message: 'This issue was closed because it has been stale for 120 days with no activity.'
+ close-pr-message: 'This PR was closed because it has been stale for 240 days with no activity.'
+ stale-issue-label: 'no-issue-activity'
+ stale-pr-label: 'no-pr-activity'
+ days-before-stale: 120
+ days-before-close: -1
+ days-before-pr-close: 240
+ exempt-issue-labels: 'gsoc,good-first-issue,long-term-plan'
+ exempt-pr-labels: 'status:ready-for-merge,status:needs-testing,status:on-hold'
+ - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
+ with:
+ stale-issue-label: 'archive'
+ days-before-stale: 240
+ exempt-issue-labels: 'gsoc,good-first-issue,long-term-plan'
+ days-before-close: -1
diff --git a/.github/workflows/ui.yml b/.github/workflows/ui.yml
index 56f757133b71..7f23add660f0 100644
--- a/.github/workflows/ui.yml
+++ b/.github/workflows/ui.yml
@@ -17,7 +17,13 @@
name: UI Build
-on: [push, pull_request]
+on:
+ push:
+ branches:
+ - main
+ - 4.22
+ - 4.20
+ pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -28,15 +34,19 @@ permissions:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false
- name: Set up Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 16
+ cache: 'npm'
+ cache-dependency-path: 'ui/package-lock.json'
- name: Env details
run: |
@@ -55,7 +65,8 @@ jobs:
npm run lint
npm run test:unit
- - uses: codecov/codecov-action@v4
+ - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
+ if: github.repository == 'apache/cloudstack'
with:
working-directory: ui
files: ./coverage/lcov.info
diff --git a/.markdownlintignore b/.markdownlintignore
new file mode 100644
index 000000000000..9fc9e21de772
--- /dev/null
+++ b/.markdownlintignore
@@ -0,0 +1 @@
+CHANGES.md
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a9bc87454f1c..91537e25267e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,23 +15,122 @@
# specific language governing permissions and limitations
# under the License.
---
-default_stages: [commit, push]
+default_stages: [pre-commit, pre-push]
default_language_version:
# force all unspecified Python hooks to run python3
python: python3
-minimum_pre_commit_version: "2.17.0"
+minimum_pre_commit_version: "3.2.0"
repos:
- repo: meta
hooks:
- id: identity
- id: check-hooks-apply
+ - repo: https://github.com/thlorenz/doctoc.git
+ rev: v2.2.0
+ hooks:
+ - id: doctoc
+ name: Add TOC for Markdown files
+ files: ^CONTRIBUTING\.md$|^INSTALL\.md$|^README\.md$
+ - repo: https://github.com/oxipng/oxipng
+ rev: v9.1.5
+ hooks:
+ - id: oxipng
+ name: run oxipng
+ description: optimize PNG images with lossless compression
+ args: ['-o', '4', '--strip', 'safe', '--alpha']
+ - repo: https://github.com/gitleaks/gitleaks
+ rev: v8.27.2
+ hooks:
+ - id: gitleaks
+ name: run gitleaks
+ description: detect hardcoded secrets
+ - repo: https://github.com/Lucas-C/pre-commit-hooks
+ rev: v1.5.5
+ hooks:
+ - id: chmod
+ name: set file permissions
+ args: ['644']
+ files: \.md$
+ stages: [manual]
+ - id: insert-license
+ name: add license for all cfg files
+ description: automatically adds a licence header to all cfg files that don't have a license header
+ files: \.cfg$
+ args:
+ - --comment-style
+ - '|#|'
+ - --license-filepath
+ - .github/workflows/license-templates/LICENSE.txt
+ - --fuzzy-match-generates-todo
+ - id: insert-license
+ name: add license for all Markdown files
+ files: \.md$
+ args:
+ - --comment-style
+ - ''
+ - --license-filepath
+ - .github/workflows/license-templates/LICENSE.txt
+ - --fuzzy-match-generates-todo
+ exclude: ^(CHANGES|ISSUE_TEMPLATE|PULL_REQUEST_TEMPLATE)\.md$|^ui/docs/(full|smoke)-test-plan\.template\.md$|^\.github/workflows/.*\.md$|^\.github/aw/.*\.md$
+ - id: insert-license
+ name: add license for all properties files
+ description: automatically adds a licence header to all properties files that don't have a license header
+ files: \.properties$
+ args:
+ - --comment-style
+ - '|#|'
+ - --license-filepath
+ - .github/workflows/license-templates/LICENSE.txt
+ - --fuzzy-match-generates-todo
+ - id: insert-license
+ name: add license for all Shell files
+ description: automatically adds a licence header to all Shell files that don't have a license header
+ files: \.sh$
+ args:
+ - --comment-style
+ - '|#|'
+ - --license-filepath
+ - .github/workflows/license-templates/LICENSE.txt
+ - --fuzzy-match-generates-todo
+ - id: insert-license
+ name: add license for all SQL files
+ files: \.sql$
+ args:
+ - --comment-style
+ - '|--|'
+ - --license-filepath
+ - .github/workflows/license-templates/LICENSE.txt
+ - --fuzzy-match-generates-todo
+ - id: insert-license
+ name: add license for all Vue files
+ files: \.vue$
+ args:
+ - --comment-style
+ - '|//|'
+ - --license-filepath
+ - .github/workflows/license-templates/LICENSE.txt
+ - --fuzzy-match-generates-todo
+ - id: insert-license
+ name: add license for all YAML files
+ description: automatically adds a licence header to all YAML files that don't have a license header
+ files: \.ya?ml$
+ args:
+ - --comment-style
+ - '|#|'
+ - --license-filepath
+ - .github/workflows/license-templates/LICENSE.txt
+ - --fuzzy-match-generates-todo
+ exclude: ^\.github/workflows/.*\.lock\.yml$
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.6.0
+ rev: v6.0.0
hooks:
#- id: check-added-large-files
- id: check-case-conflict
#- id: check-executables-have-shebangs
+ - id: check-illegal-windows-names
- id: check-merge-conflict
+ - id: check-shebang-scripts-are-executable
+ files: \.sh$
- id: check-symlinks
- id: check-vcs-permalinks
#- id: check-yaml
@@ -42,6 +141,7 @@ repos:
exclude: >
(?x)
^scripts/vm/systemvm/id_rsa\.cloud$|
+ ^server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest\.java$|
^server/src/test/java/com/cloud/keystore/KeystoreTest\.java$|
^server/src/test/resources/certs/dsa_self_signed\.key$|
^server/src/test/resources/certs/non_root\.key$|
@@ -51,47 +151,47 @@ 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$
+ exclude: \.vhd$|\.svg$
+ - id: file-contents-sorter
+ args: [--unique]
+ files: ^\.github/linters/codespell\.txt$
- id: fix-byte-order-marker
- id: forbid-submodules
- id: mixed-line-ending
- exclude: \.cs$
- id: trailing-whitespace
- files: \.(bat|cfg|cs|css|gitignore|header|in|install|java|md|properties|py|rb|sh|sql|txt|vue|xml|xsl|yaml|yml)$
+ files: ^(LICENSE|NOTICE)$|README$|\.(bat|cfg|config|cs|css|erb|gitignore|header|in|install|java|md|properties|py|rb|rc|sh|sql|svg|te|template|txt|ucls|vue|xml|xsl|yaml|yml)$|^cloud-cli/bindir/cloud-tool$|^debian/changelog$
args: [--markdown-linebreak-ext=md]
exclude: ^services/console-proxy/rdpconsole/src/test/doc/freerdp-debug-log\.txt$
- repo: https://github.com/codespell-project/codespell
- rev: v2.2.6
+ rev: v2.4.2
hooks:
- id: codespell
name: run codespell
description: Check spelling with codespell
- args: [--ignore-words=.github/linters/codespell.txt]
- exclude: ^ui/package\.json$|^ui/package-lock\.json$|^ui/public/js/less\.min\.js$|^ui/public/locales/.*[^n].*\.json$
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
args: [--config, .github/linters/.flake8]
- exclude: >
- (?x)
- ^agent/bindir/cloud-setup-agent\.in$|
- ^client/bindir/cloud-update-xenserver-licenses\.in$|
- ^cloud-cli/bindir/cloud-tool$|
- ^python/bindir/cloud-grab-dependent-library-versions$|
- ^python/bindir/cloud-setup-baremetal$|
- ^scripts/vm/hypervisor/xenserver/storagePlugin$|
- ^scripts/vm/hypervisor/xenserver/vmopspremium$|
- ^setup/bindir/cloud-setup-encryption\.in$|
- ^venv/.*$
- repo: https://github.com/igorshubovych/markdownlint-cli
- rev: v0.43.0
+ rev: v0.45.0
hooks:
- id: markdownlint
name: run markdownlint
description: check Markdown files with markdownlint
args: [--config=.github/linters/.markdown-lint.yml]
types: [markdown]
- files: \.(md|mdown|markdown)$
+ files: \.md$
+ - repo: https://github.com/adrienverge/yamllint
+ rev: v1.37.1
+ hooks:
+ - id: yamllint
+ name: run yamllint
+ description: check YAML files with yamllint
+ args: [--config-file=.github/linters/.yamllint.yml]
+ types: [yaml]
+ files: \.ya?ml$
+ exclude: ^.*k8s-.*\.ya?ml$|^.github/workflows/.*\.lock\.ya?ml$
diff --git a/CHANGES.md b/CHANGES.md
index 8fea4f3a355b..7a670a55a61f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -644,12 +644,12 @@ Bug ID | Description
Version 4.2.1
-------------
-Release notes contain the list of [bug fixes](http://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.1/html/Release_Notes/version-4.2.html#issues-fixed-4.2.1)
+Release notes contain the list of [bug fixes](https://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.1/html/Release_Notes/version-4.2.html#issues-fixed-4.2.1)
Version 4.2.0
-------------
Released on October 1 2013.
-Release notes contain the list of [bug fixes](http://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.0/html/Release_Notes/index.html)
+Release notes contain the list of [bug fixes](https://cloudstack.apache.org/docs/en-US/Apache_CloudStack/4.2.0/html/Release_Notes/index.html)
Version 4.1.0
-------------
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a6d006938f3e..f0678ed76498 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,46 +1,79 @@
-Contributing to Apache CloudStack (ACS)
-=======================================
+
+
+# Contributing to Apache CloudStack (ACS)
+
+## Summary
+
+
+
+
+- [Summary](#summary)
+- [Bug fixes](#bug-fixes)
+- [Developing new features](#developing-new-features)
+- [PendingReleaseNotes file](#pendingreleasenotes-file)
+- [Fork the code](#fork-the-code)
+- [Making changes](#making-changes)
+- [Rebase `feature_x` to include updates from `upstream/main`](#rebase-feature_x-to-include-updates-from-upstreammain)
+- [Make a GitHub Pull Request to contribute your changes](#make-a-github-pull-request-to-contribute-your-changes)
+- [Cleaning up after a successful pull request](#cleaning-up-after-a-successful-pull-request)
+- [Release Principles](#release-principles)
+
+
+
+## Summary
-Summary
--------
This document covers how to contribute to the ACS project. ACS uses GitHub PRs to manage code contributions.
-These instructions assume you have a GitHub.com account, so if you don't have one you will have to create one. Your proposed code changes will be published to your own fork of the ACS project and you will submit a Pull Request for your changes to be added.
+These instructions assume you have a GitHub.com account, so if you don't have one you will have to create one. Your proposed code changes will be published to your own fork of the ACS project, and you will submit a Pull Request for your changes to be added.
-_Lets get started!!!_
+_Let's get started!!!_
-Bug fixes
----------
+## Bug fixes
It's very important that we can easily track bug fix commits, so their hashes should remain the same in all branches.
Therefore, a pull request (PR) that fixes a bug, should be sent against a release branch.
This can be either the "current release" or the "previous release", depending on which ones are maintained.
Since the goal is a stable main, bug fixes should be "merged forward" to the next branch in order: "previous release" -> "current release" -> main (in other words: old to new)
-Developing new features
------------------------
+## Developing new features
Development should be done in a feature branch, branched off of main.
Send a PR(steps below) to get it into main (2x LGTM applies).
PR will only be merged when main is open, will be held otherwise until main is open again.
No back porting / cherry-picking features to existing branches!
-PendingReleaseNotes file
-------------------------
-When developing a new feature or making a (major) change to a existing feature you are encouraged to append this to the PendingReleaseNotes file so that the Release Manager can
+## PendingReleaseNotes file
+
+When developing a new feature or making a (major) change to an existing feature you are encouraged to append this to the PendingReleaseNotes file so that the Release Manager can
use this file as a source of information when compiling the Release Notes for a new release.
When adding information to the PendingReleaseNotes file make sure that you write a good and understandable description of the new feature or change which you have developed.
Updating the PendingReleaseNotes file is preferably a part of the original Pull Request, but that is up to the developers' discretion.
-Fork the code
--------------
+## Fork the code
In your browser, navigate to: [https://github.com/apache/cloudstack](https://github.com/apache/cloudstack)
-Fork the repository by clicking on the 'Fork' button on the top right hand side. The fork will happen and you will be taken to your own fork of the repository. Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '**HTTPS** clone URL'. You will paste this URL when doing the following `git clone` command.
+Fork the repository by clicking on the 'Fork' button on the top right hand side. The fork will happen, and you will be taken to your own fork of the repository. Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '**HTTPS** clone URL'. You will paste this URL when doing the following `git clone` command.
-On your computer, follow these steps to setup a local repository for working on ACS:
+On your computer, follow these steps to set up a local repository for working on ACS:
```bash
$ git clone https://github.com/YOUR_ACCOUNT/cloudstack.git
@@ -51,8 +84,7 @@ $ git fetch upstream
$ git rebase upstream/main
```
-Making changes
---------------
+## Making changes
It is important that you create a new branch to make changes on and that you do not change the `main` branch (other than to rebase in changes from `upstream/main`). In this example I will assume you will be making your changes to a branch called `feature_x`. This `feature_x` branch will be created on your local repository and will be pushed to your forked repository on GitHub. Once this branch is on your fork you will create a Pull Request for the changes to be added to the ACS project.
@@ -68,8 +100,7 @@ $ git commit -a -m "descriptive commit message for your changes"
> The `-b` specifies that you want to create a new branch called `feature_x`. You only specify `-b` the first time you checkout because you are creating a new branch. Once the `feature_x` branch exists, you can later switch to it with only `git checkout feature_x`.
-Rebase `feature_x` to include updates from `upstream/main`
-------------------------------------------------------------
+## Rebase `feature_x` to include updates from `upstream/main`
It is important that you maintain an up-to-date `main` branch in your local repository. This is done by rebasing in the code changes from `upstream/main` (the official ACS project repository) into your local repository. You will want to do this before you start working on a feature as well as right before you submit your changes as a pull request. I recommend you do this process periodically while you work to make sure you are working off the most recent project code.
@@ -89,12 +120,11 @@ $ git rebase main
> Now your `feature_x` branch is up-to-date with all the code in `upstream/main`.
-Make a GitHub Pull Request to contribute your changes
------------------------------------------------------
+## Make a GitHub Pull Request to contribute your changes
-When you are happy with your changes and you are ready to contribute them, you will create a Pull Request on GitHub to do so. This is done by pushing your local changes to your forked repository (default remote name is `origin`) and then initiating a pull request on GitHub.
+When you are happy with your changes, and you are ready to contribute them, you will create a Pull Request on GitHub to do so. This is done by pushing your local changes to your forked repository (default remote name is `origin`) and then initiating a pull request on GitHub.
-Please include JIRA id, detailed information about the bug/feature, what all tests are executed, how the reviewer can test this feature etc. Incase of UI PRs, a screenshot is preferred.
+Please include JIRA id, detailed information about the bug/feature, what all tests are executed, how the reviewer can test this feature etc. In case of UI PRs, a screenshot is preferred.
> **IMPORTANT:** Make sure you have rebased your `feature_x` branch to include the latest code from `upstream/main` _before_ you do this.
@@ -114,8 +144,7 @@ To initiate the pull request, do the following:
If you are requested to make modifications to your proposed changes, make the changes locally on your `feature_x` branch, re-push the `feature_x` branch to your fork. The existing pull request should automatically pick up the change and update accordingly.
-Cleaning up after a successful pull request
--------------------------------------------
+## Cleaning up after a successful pull request
Once the `feature_x` branch has been committed into the `upstream/main` branch, your local `feature_x` branch and the `origin/feature_x` branch are no longer needed. If you want to make additional changes, restart the process with a new branch.
@@ -129,6 +158,6 @@ $ git branch -D feature_x
$ git push origin :feature_x
```
-Release Principles
-------------------
+## Release Principles
+
Detailed information about ACS release principles is available at https://cwiki.apache.org/confluence/display/CLOUDSTACK/Release+principles+for+Apache+CloudStack+4.6+and+up
diff --git a/INSTALL.md b/INSTALL.md
index e133e7d7b91b..52f109b0a411 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,15 +1,46 @@
+
+
# Apache CloudStack Installation basics
This document describes how to develop, build, package and install Apache
-CloudStack. For more information please refer to the official [documentation](http://docs.cloudstack.apache.org)
+CloudStack. For more information please refer to the official [documentation](https://docs.cloudstack.apache.org)
or the developer [wiki](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Home).
Apache CloudStack developers use various platforms for development, this guide
was tested against a CentOS 7 x86_64 setup.
-* [Setting up development environment](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Setting+up+CloudStack+Development+Environment) for Apache CloudStack.
-* [Building](https://cwiki.apache.org/confluence/display/CLOUDSTACK/How+to+build+CloudStack) Apache CloudStack.
-* [Appliance based development](https://github.com/rhtyd/monkeybox)
+
+
+
+- [Setting up Development Environment](#setting-up-development-environment)
+ - [Using jenv and/or pyenv for Version Management](#using-jenv-andor-pyenv-for-version-management)
+- [Getting the Source Code](#getting-the-source-code)
+- [Building](#building)
+- [To bring up CloudStack UI](#to-bring-up-cloudstack-ui)
+- [Building with non-redistributable plugins](#building-with-non-redistributable-plugins)
+- [Packaging and Installation](#packaging-and-installation)
+ - [Debian/Ubuntu](#debianubuntu)
+ - [RHEL/CentOS](#rhelcentos)
+- [Notes](#notes)
+
+
## Setting up Development Environment
@@ -18,29 +49,30 @@ Install tools and dependencies used for development:
# yum -y install git java-17-openjdk java-17-openjdk-devel \
mysql mysql-server mkisofs git gcc python MySQL-python openssh-clients wget
-Set up Maven (3.6.0):
+Set up Maven (3.9.10):
- # wget http://www.us.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz
- # tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local
+ # wget https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz
+ # sudo tar -zxvf apache-maven-3.9.10-bin.tar.gz -C /usr/local
# cd /usr/local
- # ln -s apache-maven-3.6.3 maven
+ # sudo ln -s apache-maven-3.9.10 maven
# echo export M2_HOME=/usr/local/maven >> ~/.bashrc # or .zshrc or .profile
# echo export PATH=/usr/local/maven/bin:${PATH} >> ~/.bashrc # or .zshrc or .profile
# source ~/.bashrc
-Setup up NodeJS (LTS):
+Setup up Node.js 16:
- # curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash -
+ # curl -sL https://rpm.nodesource.com/setup_16.x | sudo -E bash -
# sudo yum install nodejs
# sudo npm install -g @vue/cli npm-check-updates
Start the MySQL service:
$ service mysqld start
+ $ mysql_secure_installation
### Using jenv and/or pyenv for Version Management
-CloudStack is built using Java and Python. To make selection of these tools versions more consistent and ease installation for developers, optional support for [jenv](http://www.jenv.be/) and [pyenv](https://github.com/yyuu/pyenv) with [virtualenv]|(https://github.com/yyuu/pyenv-virtualenv) is provided. jenv installation instructions are available here and pyenv installation instructions are available here. For users of [oh-my-zsh](http://ohmyz.sh/) there is a pyenv plugin available to trigger configuration of pyenv in a shell session.
+CloudStack is built using Java and Python. To make selection of these tools versions more consistent and ease installation for developers, optional support for [jenv](http://www.jenv.be/) and [pyenv](https://github.com/yyuu/pyenv) with [virtualenv]|(https://github.com/yyuu/pyenv-virtualenv) is provided. jenv installation instructions are available here and pyenv installation instructions are available here. For users of [oh-my-zsh](https://ohmyz.sh/) there is a pyenv plugin available to trigger configuration of pyenv in a shell session.
Following installation, execute the following commands to configure jenv and pyenv for use with CloudStack development:
@@ -86,13 +118,33 @@ Start the management server:
If this works, you've successfully setup a single server Apache CloudStack installation.
-Open the following URL on your browser to access the Management Server UI:
-
- http://localhost:8080/client/
+To access the Management Server UI, follow the following procedure:
The default credentials are; user: admin, password: password and the domain
field should be left blank which is defaulted to the ROOT domain.
+## To bring up CloudStack UI
+
+Move to UI Directory
+
+ $ cd /path/to/cloudstack/ui
+
+To install dependencies.
+
+ $ npm install
+
+To build the project.
+
+ $ npm run build
+
+For Development Mode.
+
+ $ npm start
+
+Make sure to set `CS_URL=http://localhost:8080` on the `.env.local` file on UI.
+
+You should be able to run the management server on http://localhost:5050
+
## Building with non-redistributable plugins
CloudStack supports several plugins that depend on libraries with distribution restrictions.
@@ -150,7 +202,7 @@ All the rpm packages will be created in `dist/rpmbuild/RPMS/x86_64` directory.
## Notes
-If you will be using Xen as your hypervisor, please download [vhd-util](http://download.cloudstack.org/tools/vhd-util)
+If you will be using Xen as your hypervisor, please download [vhd-util](https://download.cloudstack.org/tools/vhd-util)
If management server is installed on RHEL/CentOS, then copy vhd-util into:
diff --git a/LICENSE b/LICENSE
index 8be7d8083a5e..e61c431f5fad 100644
--- a/LICENSE
+++ b/LICENSE
@@ -177,14 +177,14 @@ Copyright (c) 2014 The Apache Software Foundation
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
-
+
This distribution contains third party resources.
Within the console-proxy/js directory
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
- Copyright (c) 2009, John Resig
-
+ Copyright (c) 2009, John Resig
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -192,10 +192,10 @@ Within the console-proxy/js directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -203,43 +203,43 @@ Within the console-proxy/js directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from John Resig
- jquery.js
+
+ from John Resig
+ jquery.js
Within the systemvm/debian/etc directory
placed in the public domain
- by Adiscon GmbH http://www.adiscon.com/
- rsyslog.conf
- by Simon Kelley
- dnsmasq.conf
- vpcdnsmasq.conf
+ by Adiscon GmbH http://www.adiscon.com/
+ rsyslog.conf
+ by Simon Kelley
+ dnsmasq.conf
+ vpcdnsmasq.conf
Within the systemvm/debian/etc/apache2 directory
licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above)
Copyright (c) 2012 The Apache Software Foundation
- from The Apache Software Foundation http://www.apache.org/
- httpd.conf
+ from The Apache Software Foundation http://www.apache.org/
+ httpd.conf
vhost.template
Within the systemvm/debian/etc/ssh/ directory
licensed under the BSD (2-clause) http://www.opensource.org/licenses/BSD-2-Clause (as follows)
-
-
+
+
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
-
+
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer. Redistributions in binary form must
reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the
distribution.
-
+
Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
-
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -250,55 +250,55 @@ Within the systemvm/debian/etc/ssh/ directory
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- from OpenSSH Project http://www.openssh.org/
- sshd_config
+
+ from OpenSSH Project http://www.openssh.org/
+ sshd_config
Within the systemvm/debian/root/redundant_router directory
placed in the public domain
- by The netfilter.org project http://www.netfilter.org/
- conntrackd.conf.templ
+ by The netfilter.org project http://www.netfilter.org/
+ conntrackd.conf.templ
Within the scripts/storage/secondary directory
licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above)
Copyright (c) 2010-2011 OpenStack, LLC.
- from OpenStack, LLC http://www.openstack.org
- swift
+ from OpenStack, LLC http://www.openstack.org
+ swift
Within the scripts/vm/hypervisor/xenserver directory
licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above)
Copyright (c) 2010-2011 OpenStack, LLC.
- from OpenStack, LLC http://www.openstack.org
- swift
+ from OpenStack, LLC http://www.openstack.org
+ swift
Within the ui/lib directory
placed in the public domain
- by Eric Meyer http://meyerweb.com/eric/
+ by Eric Meyer http://meyerweb.com/eric/
reset.css from http://meyerweb.com/eric/tools/css/reset/
licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above)
Copyright (c) 2006 Google Inc.
- from Google Inc. http://google.com
+ from Google Inc. http://google.com
excanvas.js from http://code.google.com/p/explorercanvas/
licensed under the BSD (2-clause) http://www.opensource.org/licenses/BSD-2-Clause (as follows)
Copyright (c) 2008 George McGinley Smith
- All rights reserved.
-
+ All rights reserved.
+
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
-
+
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer. Redistributions in binary form must
reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the
distribution.
-
+
Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
-
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -309,13 +309,13 @@ Within the ui/lib directory
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- from George McGinley Smith
- jquery.easing.js
+
+ from George McGinley Smith
+ jquery.easing.js
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
-
-
+
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -323,10 +323,10 @@ Within the ui/lib directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -334,14 +334,14 @@ Within the ui/lib directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from The Dojo Foundation http://dojofoundation.org/
+
+ from The Dojo Foundation http://dojofoundation.org/
require.js from http://github.com/jrburke/requirejs
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
- Copyright (c) 2011, John Resig
-
+ Copyright (c) 2011, John Resig
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -349,10 +349,10 @@ Within the ui/lib directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -360,14 +360,14 @@ Within the ui/lib directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from John Resig
- jquery.js
+
+ from John Resig
+ jquery.js
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
Copyright (c) 2014 Jörn Zaefferer
-
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -375,10 +375,10 @@ Within the ui/lib directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -386,9 +386,9 @@ Within the ui/lib directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from Jorn Zaefferer
- jquery.validate.js
+
+ from Jorn Zaefferer
+ jquery.validate.js
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
@@ -418,8 +418,8 @@ Within the ui/lib directory
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
- Copyright (c) 2010, Sebastian Tschan
-
+ Copyright (c) 2010, Sebastian Tschan
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -427,10 +427,10 @@ Within the ui/lib directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -438,14 +438,14 @@ Within the ui/lib directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from Sebastian Tschan https://blueimp.net
- jquery.md5.js
+
+ from Sebastian Tschan https://blueimp.net
+ jquery.md5.js
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
- Copyright (c) 2006 Klaus Hartl (stilbuero.de)
-
+ Copyright (c) 2006 Klaus Hartl (stilbuero.de)
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -453,10 +453,10 @@ Within the ui/lib directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -464,15 +464,15 @@ Within the ui/lib directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from Klaus Hartl http://stilbuero.de
- jquery.cookies.js
+
+ from Klaus Hartl http://stilbuero.de
+ jquery.cookies.js
Within the ui/lib/flot directory
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
- Released under the MIT license by IOLA, December 2007.
-
+ Released under the MIT license by IOLA, December 2007.
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -480,10 +480,10 @@ Within the ui/lib/flot directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -491,24 +491,24 @@ Within the ui/lib/flot directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from IOLA http://www.iola.dk/
- jquery.flot.crosshair.js
- jquery.flot.fillbetween.js
- jquery.flot.image.js
- jquery.flot.js
- jquery.flot.navigate.js
- jquery.flot.resize.js
- jquery.flot.selection.js
- jquery.flot.stack.js
- jquery.flot.symbol.js
- jquery.flot.threshold.js
+
+ from IOLA http://www.iola.dk/
+ jquery.flot.crosshair.js
+ jquery.flot.fillbetween.js
+ jquery.flot.image.js
+ jquery.flot.js
+ jquery.flot.navigate.js
+ jquery.flot.resize.js
+ jquery.flot.selection.js
+ jquery.flot.stack.js
+ jquery.flot.symbol.js
+ jquery.flot.threshold.js
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
Created by Brian Medendorp, June 2009
- Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
-
+ Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -516,10 +516,10 @@ Within the ui/lib/flot directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -527,13 +527,13 @@ Within the ui/lib/flot directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from Brian Medendorp
- jquery.pie.js
+
+ from Brian Medendorp
+ jquery.pie.js
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
-
-
+
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -541,10 +541,10 @@ Within the ui/lib/flot directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -552,14 +552,14 @@ Within the ui/lib/flot directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from Ole Laursen
- jquery.colorhelpers.js
+
+ from Ole Laursen
+ jquery.colorhelpers.js
Within the ui/lib/jquery-ui directory
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
-
-
+
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -567,10 +567,10 @@ Within the ui/lib/jquery-ui directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -578,17 +578,17 @@ Within the ui/lib/jquery-ui directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from jQuery UI Developers http://jqueryui.com/about
- css/jquery-ui.css
- index.html
- js/jquery-ui.js
+
+ from jQuery UI Developers http://jqueryui.com/about
+ css/jquery-ui.css
+ index.html
+ js/jquery-ui.js
Within the ui/lib/qunit directory
licensed under the MIT License http://www.opensource.org/licenses/mit-license.php (as follows)
- Copyright (c) 2012 John Resig, Jörn Zaefferer
-
+ Copyright (c) 2012 John Resig, Jörn Zaefferer
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -596,10 +596,10 @@ Within the ui/lib/qunit directory
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -607,20 +607,20 @@ Within the ui/lib/qunit directory
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- from Jorn Zaefferer
+
+ from Jorn Zaefferer
qunit.css from http://docs.jquery.com/QUnit
qunit.js from http://docs.jquery.com/QUnit
Within the utils/src/main/java/com/cloud/utils/db directory
licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above)
Copyright (c) 2004 Clinton Begin
- from Clinton Begin http://code.google.com/p/mybatis/
+ from Clinton Begin http://code.google.com/p/mybatis/
ScriptRunner.java from http://code.google.com/p/mybatis/
Within the utils/src/main/java/org/apache/commons/httpclient/contrib/ssl directory
licensed under the Apache License, Version 2 http://www.apache.org/licenses/LICENSE-2.0.txt (as above)
Copyright (c) 2007 The Apache Software Foundation
- from The Apache Software Foundation http://www.apache.org/
- EasySSLProtocolSocketFactory.java
- EasyX509TrustManager.java
+ from The Apache Software Foundation http://www.apache.org/
+ EasySSLProtocolSocketFactory.java
+ EasyX509TrustManager.java
diff --git a/NOTICE b/NOTICE
index b19e4a428530..8666be264b51 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,62 +1,62 @@
Apache CloudStack
Copyright 2014 The Apache Software Foundation
-
+
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
-
-
+
+
This distribution contains third party resources requiring the following notices:
-
- For
+
+ For
jquery.js
-
+
jQuery JavaScript Library v1.3.2
http://jquery.com/
-
+
Copyright (c) 2009 John Resig
Dual licensed under the MIT and GPL licenses.
http://docs.jquery.com/License
-
+
Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
Revision: 6246
-
- For
+
+ For
jquery.js
-
+
jQuery JavaScript Library v1.6.4
http://jquery.com/
-
+
Copyright 2011, John Resig
Dual licensed under the MIT or GPL Version 2 licenses.
http://jquery.org/license
-
+
Includes Sizzle.js
http://sizzlejs.com/
Copyright 2011, The Dojo Foundation
Released under the MIT, BSD, and GPL Licenses.
-
+
Date: Mon Sep 12 18:54:48 2011 -0400
-
- For
+
+ For
jquery.md5.js
-
+
jQuery MD5 Plugin 1.2.1
https://github.com/blueimp/jQuery-MD5
-
+
Copyright 2010, Sebastian Tschan
https://blueimp.net
-
+
Licensed under the MIT license:
http://creativecommons.org/licenses/MIT/
-
+
Based on
A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
Digest Algorithm, as defined in RFC 1321.
@@ -65,15 +65,15 @@ Copyright 2014 The Apache Software Foundation
Distributed under the BSD License
See http://pajhome.org.uk/crypt/md5 for more info.
-
- For
+
+ For
jquery.colorhelpers.js
-
+
Plugin for jQuery for working with colors.
-
+
Version 1.1.
-
+
Inspiration from jQuery color animation plugin by John Resig.
-
+
Released under the MIT license by Ole Laursen, October 2009.
diff --git a/PRE_COMMIT.md b/PRE_COMMIT.md
new file mode 100644
index 000000000000..62dc296c99e4
--- /dev/null
+++ b/PRE_COMMIT.md
@@ -0,0 +1,62 @@
+
+
+# pre-commit
+
+We run [pre-commit](https://pre-commit.com/) with
+[GitHub Actions](https://github.com/apache/cloudstack/blob/main/.github/workflows/pre-commit.yml) so installation on your
+local machine is currently optional.
+
+The `pre-commit` [configuration file](https://github.com/apache/cloudstack/blob/main/.pre-commit-config.yaml)
+is in the repository root. Before you can run the hooks, you need to have `pre-commit` installed. `pre-commit` is a
+[Python package](https://pypi.org/project/pre-commit/).
+
+From the repository root run: `pip install -r requirements-dev.txt` to install `pre-commit` and after you install
+`pre-commit` you will then need to install the pre-commit hooks by running `pre-commit install`.
+
+The hooks run when running `git commit` and also from the command line with `pre-commit`. Some of the hooks will auto
+fix the code after the hooks fail whilst most will print error messages from the linters. If a hook fails the overall
+commit will fail, and you will need to fix the issues or problems and `git add` and `git commit` again. On `git commit`
+the hooks will run mostly only against modified files so if you want to test all hooks against all files and when you
+are adding a new hook you should always run:
+
+`pre-commit run --all-files`
+
+Sometimes you might need to skip a hook to commit because the hook is stopping you from committing or your computer
+might not have all the installation requirements for all the hooks. The `SKIP` variable is comma separated for two or
+more hooks:
+
+`SKIP=codespell git commit -m "foo"`
+
+The same applies when running pre-commit:
+
+`SKIP=codespell pre-commit run --all-files`
+
+Occasionally you can have more serious problems when using `pre-commit` with `git commit`. You can use `--no-verify` to
+commit and stop `pre-commit` from checking the hooks. For example:
+
+`git commit --no-verify -m "foo"`
+
+If you are having major problems using `pre-commit` you can always uninstall it.
+
+To run a single hook use `pre-commit run --all-files `
+
+For example just run the `codespell` hook:
+
+`pre-commit run --all-files codespell`
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
index 0bd7a7643ce1..be04436d3ff8 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -22,8 +22,8 @@ This PR...
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] Enhancement (improves an existing feature and functionality)
- [ ] Cleanup (Code refactoring and cleanup, that may add test cases)
-- [ ] build/CI
-- [ ] test (unit or integration test code)
+- [ ] Build/CI
+- [ ] Test (unit or integration test code)
### Feature/Enhancement Scale or Bug Severity
diff --git a/README.md b/README.md
index f66a4dc6f975..852674ab7b1f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,54 @@
-# Apache CloudStack [](https://github.com/apache/cloudstack/actions/workflows/build.yml) [](https://github.com/apache/cloudstack/actions/workflows/ui.yml) [](https://github.com/apache/cloudstack/actions/workflows/rat.yml) [](https://github.com/apache/cloudstack/actions/workflows/ci.yml) [](https://sonarcloud.io/dashboard?id=apache_cloudstack) [](https://codecov.io/gh/apache/cloudstack)
+
+
+# Apache CloudStack
+
+[](https://github.com/apache/cloudstack/actions/workflows/build.yml)
+[](https://codecov.io/gh/apache/cloudstack)
+[](https://github.com/apache/cloudstack/actions/workflows/docker-cloudstack-simulator.yml)
+[](https://github.com/apache/cloudstack/actions/workflows/rat.yml)
+[](https://github.com/apache/cloudstack/actions/workflows/linter.yml)
+[](https://github.com/apache/cloudstack/actions/workflows/merge-conflict-checker.yml)
+[](https://sonarcloud.io/dashboard?id=apache_cloudstack)
+[](https://github.com/apache/cloudstack/actions/workflows/ci.yml)
+[](https://github.com/apache/cloudstack/actions/workflows/ui.yml)
[](https://cloudstack.apache.org/)
+
+
+
+- [Who Uses CloudStack?](#who-uses-cloudstack)
+- [Demo](#demo)
+- [Getting Started](#getting-started)
+- [Getting Source Repository](#getting-source-repository)
+- [Documentation](#documentation)
+- [News and Events](#news-and-events)
+- [Getting Involved and Contributing](#getting-involved-and-contributing)
+- [Reporting Security Vulnerabilities](#reporting-security-vulnerabilities)
+- [License](#license)
+- [Notice of Cryptographic Software](#notice-of-cryptographic-software)
+- [Star History](#star-history)
+- [Contributors](#contributors)
+
+
+
Apache CloudStack is open source software designed to deploy and manage large
networks of virtual machines, as a highly available, highly scalable
Infrastructure as a Service (IaaS) cloud computing platform. CloudStack is used
@@ -21,7 +68,7 @@ OVM and LXC containers.
Users can manage their cloud with an easy to use Web interface, command line
tools, and/or a full-featured query based API.
-For more information on Apache CloudStack, please visit the [website](http://cloudstack.apache.org)
+For more information on Apache CloudStack, please visit the [website](https://cloudstack.apache.org)
## Who Uses CloudStack?
@@ -78,10 +125,10 @@ via GitHub pull requests.
## Getting Involved and Contributing
Interested in helping out with Apache CloudStack? Great! We welcome
-participation from anybody willing to work [The Apache Way](http://theapacheway.com) and make a
+participation from anybody willing to work [The Apache Way](https://theapacheway.com) and make a
contribution. Note that you do not have to be a developer in order to contribute
to Apache CloudStack. We need folks to help with documentation, translation,
-promotion etc. See our contribution [page](http://cloudstack.apache.org/contribute.html).
+promotion etc. See our contribution [page](https://cloudstack.apache.org/contribute.html).
If you are a frequent contributors, you can request to be added as collaborators
(see https://cwiki.apache.org/confluence/display/INFRA/Git+-+.asf.yaml+features#Git.asf.yamlfeatures-AssigningexternalcollaboratorswiththetriageroleonGitHub)
@@ -92,7 +139,7 @@ You may do so by sharing your GitHub users ID or raise a GitHub issue.
If you're interested in learning more or participating in the Apache CloudStack
project, the mailing lists are the best way to do that. While the project has
-several communications channels, the [mailing lists](http://cloudstack.apache.org/mailing-lists.html) are the most active and the
+several communications channels, the [mailing lists](https://cloudstack.apache.org/mailing-lists.html) are the most active and the
official channels for making decisions about the project itself.
Mailing lists:
@@ -112,7 +159,7 @@ released version of CloudStack, please report it to `security@apache.org` with
details about the vulnerability, how it might be exploited, and any additional
information that might be useful.
-For more details, please visit our security [page](http://cloudstack.apache.org/security.html).
+For more details, please visit our security [page](https://cloudstack.apache.org/security.html).
## License
@@ -156,7 +203,15 @@ Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Sec
The following provides more details on the included cryptographic software:
* CloudStack makes use of JaSypt cryptographic libraries.
-* CloudStack has a system requirement of MySQL, and uses native database encryption functionality.
+* CloudStack requires a MySQL-compatible database (MariaDB or MySQL), and uses native database encryption functionality.
* CloudStack makes use of the Bouncy Castle general-purpose encryption library.
* CloudStack can optionally interact with and control OpenSwan-based VPNs.
* CloudStack has a dependency on and makes use of JSch - a java SSH2 implementation.
+
+## Star History
+
+[](https://www.star-history.com/#apache/cloudstack&Date)
+
+## Contributors
+
+[](https://github.com/apache/cloudstack/graphs/contributors)
diff --git a/agent/bindir/cloud-setup-agent.in b/agent/bindir/cloud-setup-agent.in
index 18de64089ed0..dc4582540df9 100755
--- a/agent/bindir/cloud-setup-agent.in
+++ b/agent/bindir/cloud-setup-agent.in
@@ -20,9 +20,22 @@ import os
import logging
import sys
import socket
+
+# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ----
+# ---- We do this so cloud_utils can be looked up in the following order:
+# ---- 1) Sources directory
+# ---- 2) waf configured PYTHONDIR
+# ---- 3) System Python path
+for pythonpath in (
+ "@PYTHONDIR@",
+ os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"),
+ ):
+ if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath)
+# ---- End snippet of code ----
+
from cloudutils.cloudException import CloudRuntimeException, CloudInternalException
from cloudutils.utilities import initLoging, bash
-from cloudutils.configFileOps import configFileOps
+from cloudutils.configFileOps import configFileOps
from cloudutils.globalEnv import globalEnv
from cloudutils.networkConfig import networkConfig
from cloudutils.syscfg import sysConfigFactory
@@ -30,35 +43,41 @@ from cloudutils.serviceConfig import configureLibvirtConfig, configure_libvirt_t
from optparse import OptionParser
+
def getUserInputs():
print("Welcome to the CloudStack Agent Setup:")
cfo = configFileOps("@AGENTSYSCONFDIR@/agent.properties")
oldMgt = cfo.getEntry("host")
- mgtSvr = input("Please input the Management Server Hostname/IP-Address:[%s]"%oldMgt)
+ mgtSvr = input(
+ "Please input the Management Server Hostname/IP-Address:[%s]" % oldMgt
+ )
if mgtSvr == "":
mgtSvr = oldMgt
try:
socket.getaddrinfo(mgtSvr, 443)
except:
- print("Failed to resolve %s. Please input a valid hostname or IP-Address."%mgtSvr)
+ print(
+ "Failed to resolve %s. Please input a valid hostname or IP-Address."
+ % mgtSvr
+ )
exit(1)
oldToken = cfo.getEntry("zone")
- zoneToken = input("Please input the Zone Id:[%s]"%oldToken)
+ zoneToken = input("Please input the Zone Id:[%s]" % oldToken)
if zoneToken == "":
zoneToken = oldToken
oldPod = cfo.getEntry("pod")
- podId = input("Please input the Pod Id:[%s]"%oldPod)
+ podId = input("Please input the Pod Id:[%s]" % oldPod)
if podId == "":
- podId = oldToken
+ podId = oldToken
oldCluster = cfo.getEntry("cluster")
- clusterId = input("Please input the Cluster Id:[%s]"%oldCluster)
+ clusterId = input("Please input the Cluster Id:[%s]" % oldCluster)
if clusterId == "":
clusterId = oldCluster
@@ -66,18 +85,20 @@ def getUserInputs():
if oldHypervisor == "":
oldHypervisor = "kvm"
- hypervisor = input("Please input the Hypervisor type kvm/lxc:[%s]"%oldHypervisor)
+ hypervisor = input("Please input the Hypervisor type kvm/lxc:[%s]" % oldHypervisor)
if hypervisor == "":
hypervisor = oldHypervisor
try:
defaultNic = networkConfig.getDefaultNetwork()
except:
- print("Failed to get default route. Please configure your network to have a default route")
+ print(
+ "Failed to get default route. Please configure your network to have a default route"
+ )
exit(1)
defNic = defaultNic.name
- network = input("Please choose which network used to create VM:[%s]"%defNic)
+ network = input("Please choose which network used to create VM:[%s]" % defNic)
if network == "":
if defNic == "":
print("You need to specify one of Nic or bridge on your system")
@@ -87,7 +108,8 @@ def getUserInputs():
return [mgtSvr, zoneToken, network, podId, clusterId, hypervisor]
-if __name__ == '__main__':
+
+if __name__ == "__main__":
initLoging("@AGENTLOGDIR@/setup.log")
glbEnv = globalEnv()
@@ -95,13 +117,23 @@ if __name__ == '__main__':
glbEnv.agentMode = "Agent"
parser = OptionParser()
parser.add_option("-a", action="store_true", dest="auto", help="auto mode")
- parser.add_option("-m", "--host", dest="mgt", help="Management server hostname or IP-Address")
+ parser.add_option(
+ "-m", "--host", dest="mgt", help="Management server hostname or IP-Address"
+ )
parser.add_option("-z", "--zone", dest="zone", help="zone id")
parser.add_option("-p", "--pod", dest="pod", help="pod id")
parser.add_option("-c", "--cluster", dest="cluster", help="cluster id")
- parser.add_option("-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type")
+ parser.add_option(
+ "-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type"
+ )
parser.add_option("-g", "--guid", dest="guid", help="guid")
- parser.add_option("-s", action="store_true", default=False, dest="secure", help="Secure and enable TLS for libvirtd")
+ parser.add_option(
+ "-s",
+ action="store_true",
+ default=False,
+ dest="secure",
+ help="Secure and enable TLS for libvirtd",
+ )
parser.add_option("--pubNic", dest="pubNic", help="Public traffic interface")
parser.add_option("--prvNic", dest="prvNic", help="Private traffic interface")
parser.add_option("--guestNic", dest="guestNic", help="Guest traffic interface")
@@ -127,15 +159,15 @@ if __name__ == '__main__':
glbEnv.pod = userInputs[3]
glbEnv.cluster = userInputs[4]
glbEnv.hypervisor = userInputs[5]
- #generate UUID
+ # generate UUID
glbEnv.uuid = old_config.getEntry("guid")
if glbEnv.uuid == "":
glbEnv.uuid = bash("uuidgen").getStdout()
else:
for para, value in list(options.__dict__.items()):
if value is None:
- print("Missing operand:%s"%para)
- print("Try %s --help for more information"%sys.argv[0])
+ print("Missing operand:%s" % para)
+ print("Try %s --help for more information" % sys.argv[0])
sys.exit(1)
glbEnv.uuid = options.guid
@@ -155,7 +187,7 @@ if __name__ == '__main__':
try:
syscfg.config()
print("CloudStack Agent setup is done!")
- except (CloudRuntimeException,CloudInternalException) as e:
+ except (CloudRuntimeException, CloudInternalException) as e:
print(e)
print("Try to restore your system:")
try:
diff --git a/agent/bindir/libvirtqemuhook.in b/agent/bindir/libvirtqemuhook.in
index e17944d83537..4cc6ed7a1d28 100755
--- a/agent/bindir/libvirtqemuhook.in
+++ b/agent/bindir/libvirtqemuhook.in
@@ -20,6 +20,19 @@ import sys
import os
import subprocess
from threading import Timer
+
+# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ----
+# ---- We do this so cloud_utils can be looked up in the following order:
+# ---- 1) Sources directory
+# ---- 2) waf configured PYTHONDIR
+# ---- 3) System Python path
+for pythonpath in (
+ "@PYTHONDIR@",
+ os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"),
+ ):
+ if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath)
+# ---- End snippet of code ----
+
from xml.dom.minidom import parse
from cloudutils.configFileOps import configFileOps
from cloudutils.networkConfig import networkConfig
diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index bff7078fd9f9..2d244e00edaf 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -78,6 +78,14 @@ zone=default
# Generated with "uuidgen".
local.storage.uuid=
+# Enable TLS for image server transfers. The keys are read from:
+# cert file = /etc/cloudstack/agent/cloud.crt
+# key file = /etc/cloudstack/agent/cloud.key
+image.server.tls.enabled=true
+
+# The Address for the network interface that the image server listens on. If not specified, it will listen on the Management network.
+#image.server.listen.address=
+
# Location for KVM virtual router scripts.
# The path defined in this property is relative to the directory "/usr/share/cloudstack-common/".
domr.scripts.dir=scripts/network/domr/kvm
@@ -213,8 +221,9 @@ hypervisor.type=kvm
# If null (default), defaults to the VM's OS architecture
#guest.cpu.arch=
-# This param will require CPU features on the CPU section.
-# The features listed in this property must be separated by a blank space (e.g.: vmx vme)
+# Specifies required CPU features for end-user and system VMs.
+# These features must be present on the host CPU for VM deployment.
+# Multiple features should be separated by whitespace (e.g.: vmx vme).
#guest.cpu.features=
# Disables memory ballooning on VM guests for overcommit.
@@ -307,7 +316,7 @@ iscsi.session.cleanup.enabled=false
#vm.migrate.domain.retrieve.timeout=10
# This parameter specifies if the host must be rebooted when something goes wrong with the heartbeat.
-#reboot.host.and.alert.management.on.heartbeat.timeout=true
+#reboot.host.and.alert.management.on.heartbeat.timeout=false
# Enables manually setting CPU's topology on KVM's VM.
#enable.manually.setting.cpu.topology.on.kvm.vm=true
@@ -363,6 +372,14 @@ iscsi.session.cleanup.enabled=false
# to the directory "/usr/share/cloudstack-common/".
#network.scripts.dir=scripts/vm/network/vnet
+# Sets the VXLAN networking mode used, either 'multicast' (default) or 'evpn'.
+# The different modes lead to different scripts being executed by the Agent.
+# multicast: modifyvxlan.sh
+# evpn: modifyvxlan-evpn.sh
+# Existing environments using VXLAN can safely switch to the 'evpn' mode as this
+# will not break existing functionality.
+#network.vxlan.mode=multicast
+
# Defines the location for storage scripts.
# The path defined in this property is relative.
# To locate the script, ACS first tries to concatenate
@@ -441,3 +458,33 @@ iscsi.session.cleanup.enabled=false
# Wait(in seconds) during agent reconnections. When no value is set then default value of 5s will be used
#backoff.seconds=
+
+# Timeout (in seconds) to wait for the snapshot reversion to complete.
+# revert.snapshot.timeout=10800
+
+# Timeout (in seconds) to wait for the incremental snapshot to complete.
+# incremental.snapshot.timeout=10800
+
+# If set to true, creates VMs as full clones of their templates on KVM hypervisor. Creates as linked clones otherwise.
+# create.full.clone=false
+
+# Instance conversion TMPDIR env var
+#convert.instance.env.tmpdir=
+
+# Instance conversion VIRT_V2V_TMPDIR env var
+#convert.instance.env.virtv2v.tmpdir=
+
+# Time, in seconds, to wait before retrying to rebase during the incremental snapshot process.
+# incremental.snapshot.retry.rebase.wait=60
+
+# Path to the VDDK library directory for VMware to KVM conversion via VDDK,
+# passed to virt-v2v as -io vddk-libdir=
+#vddk.lib.dir=
+
+# Ordered VDDK transport preference for VMware to KVM conversion via VDDK, passed as
+# -io vddk-transports= to virt-v2v. Example: nbd:nbdssl
+#vddk.transports=
+
+# Optional vCenter SHA1 thumbprint for VMware to KVM conversion via VDDK, passed as
+# -io vddk-thumbprint=. If unset, CloudStack computes it on the KVM host via openssl.
+#vddk.thumbprint=
diff --git a/agent/conf/developer.properties.template b/agent/conf/developer.properties.template
index a70a136f38cd..02f51aa6bb85 100644
--- a/agent/conf/developer.properties.template
+++ b/agent/conf/developer.properties.template
@@ -5,9 +5,9 @@
# 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
diff --git a/agent/conf/uefi.properties.in b/agent/conf/uefi.properties.in
new file mode 100644
index 000000000000..3c8866f634bc
--- /dev/null
+++ b/agent/conf/uefi.properties.in
@@ -0,0 +1,24 @@
+# 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.
+
+# Configuration file for UEFI
+
+guest.nvram.template.legacy=@GUESTNVRAMTEMPLATELEGACY@
+guest.loader.legacy=@GUESTLOADERLEGACY@
+guest.nvram.template.secure=@GUESTNVRAMTEMPLATESECURE@
+guest.loader.secure=@GUESTLOADERSECURE@
+guest.nvram.path=@GUESTNVRAMPATH@
diff --git a/agent/pom.xml b/agent/pom.xml
index 76c37cd87be3..5ab6cfe17c13 100644
--- a/agent/pom.xml
+++ b/agent/pom.xml
@@ -24,7 +24,7 @@
org.apache.cloudstack
cloudstack
- 4.21.0.0-SNAPSHOT
+ 4.23.0.0-SNAPSHOT
diff --git a/agent/src/main/java/com/cloud/agent/Agent.java b/agent/src/main/java/com/cloud/agent/Agent.java
index 0a76bfbb4f8e..275fd41edc34 100644
--- a/agent/src/main/java/com/cloud/agent/Agent.java
+++ b/agent/src/main/java/com/cloud/agent/Agent.java
@@ -97,7 +97,6 @@
import com.cloud.utils.nio.NioClient;
import com.cloud.utils.nio.NioConnection;
import com.cloud.utils.nio.Task;
-import com.cloud.utils.script.OutputInterpreter;
import com.cloud.utils.script.Script;
/**
@@ -342,7 +341,7 @@ public void start() {
logger.info("Attempted to connect to the server, but received an unexpected exception, trying again...", e);
}
}
- shell.updateConnectedHost();
+ shell.updateConnectedHost(((NioClient)connection).getHost());
scavengeOldAgentObjects();
}
@@ -453,22 +452,30 @@ private void scheduleCertificateRenewalTask() {
certExecutor.schedule(new PostCertificateRenewalTask(this), 5, TimeUnit.SECONDS);
}
- private void scheduleHostLBCheckerTask(final long checkInterval) {
+ private void scheduleHostLBCheckerTask(final String lbAlgorithm, final long checkInterval) {
String name = "HostLBCheckerTask";
if (hostLbCheckExecutor != null && !hostLbCheckExecutor.isShutdown()) {
+ logger.info("Shutting down the preferred host checker task {}", name);
hostLbCheckExecutor.shutdown();
try {
if (!hostLbCheckExecutor.awaitTermination(1, TimeUnit.SECONDS)) {
hostLbCheckExecutor.shutdownNow();
}
} catch (InterruptedException e) {
- logger.debug("Forcing {} shutdown as it did not shutdown in the desired time due to: {}",
+ logger.debug("Forcing the preferred host checker task {} shutdown as it did not shutdown in the desired time due to: {}",
name, e.getMessage());
hostLbCheckExecutor.shutdownNow();
}
}
if (checkInterval > 0L) {
- logger.info("Scheduling preferred host task with host.lb.interval={}ms", checkInterval);
+ if ("shuffle".equalsIgnoreCase(lbAlgorithm)) {
+ logger.info("Scheduling the preferred host checker task to trigger once (to apply lb algorithm '{}') after host.lb.interval={} ms", lbAlgorithm, checkInterval);
+ hostLbCheckExecutor = Executors.newSingleThreadScheduledExecutor((new NamedThreadFactory(name)));
+ hostLbCheckExecutor.schedule(new PreferredHostCheckerTask(), checkInterval, TimeUnit.MILLISECONDS);
+ return;
+ }
+
+ logger.info("Scheduling a recurring preferred host checker task with host.lb.interval={} ms", checkInterval);
hostLbCheckExecutor = Executors.newSingleThreadScheduledExecutor((new NamedThreadFactory(name)));
hostLbCheckExecutor.scheduleAtFixedRate(new PreferredHostCheckerTask(), checkInterval, checkInterval,
TimeUnit.MILLISECONDS);
@@ -606,9 +613,9 @@ protected void setupStartupCommand(final StartupCommand startup) {
}
protected String getAgentArch() {
- final Script command = new Script("/usr/bin/arch", 500, logger);
- final OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser();
- return command.execute(parser);
+ String arch = Script.runSimpleBashScript(Script.getExecutableAbsolutePath("arch"), 2000);
+ logger.debug("Arch for agent: {} found: {}", _name, arch);
+ return arch;
}
@Override
@@ -617,15 +624,11 @@ public Task create(final Task.Type type, final Link link, final byte[] data) {
}
protected void reconnect(final Link link) {
- reconnect(link, null, null, false);
+ reconnect(link, null, false);
}
- protected void reconnect(final Link link, String preferredHost, List avoidHostList, boolean forTransfer) {
+ protected void reconnect(final Link link, String preferredMSHost, boolean forTransfer) {
if (!(forTransfer || reconnectAllowed)) {
- return;
- }
-
- if (!reconnectAllowed) {
logger.debug("Reconnect requested but it is not allowed {}", () -> getLinkLog(link));
return;
}
@@ -637,19 +640,26 @@ protected void reconnect(final Link link, String preferredHost, List avo
serverResource.disconnected();
logger.info("Lost connection to host: {}. Attempting reconnection while we still have {} commands in progress.", shell.getConnectedHost(), commandsInProgress.get());
stopAndCleanupConnection(true);
+ String host = preferredMSHost;
+ if (org.apache.commons.lang3.StringUtils.isBlank(host)) {
+ host = shell.getNextHost();
+ }
+ List avoidMSHostList = shell.getAvoidHosts();
do {
- final String host = shell.getNextHost();
- connection = new NioClient(getAgentName(), host, shell.getPort(), shell.getWorkers(), shell.getSslHandshakeTimeout(), this);
- logger.info("Reconnecting to host: {}", host);
- try {
- connection.start();
- } catch (final NioConnectionException e) {
- logger.info("Attempted to re-connect to the server, but received an unexpected exception, trying again...", e);
- stopAndCleanupConnection(false);
+ if (CollectionUtils.isEmpty(avoidMSHostList) || !avoidMSHostList.contains(host)) {
+ connection = new NioClient(getAgentName(), host, shell.getPort(), shell.getWorkers(), shell.getSslHandshakeTimeout(), this);
+ logger.info("Reconnecting to host: {}", host);
+ try {
+ connection.start();
+ } catch (final NioConnectionException e) {
+ logger.info("Attempted to re-connect to the server, but received an unexpected exception, trying again...", e);
+ stopAndCleanupConnection(false);
+ }
}
shell.getBackoffAlgorithm().waitBeforeRetry();
+ host = shell.getNextHost();
} while (!connection.isStartup());
- shell.updateConnectedHost();
+ shell.updateConnectedHost(((NioClient)connection).getHost());
logger.info("Connected to the host: {}", shell.getConnectedHost());
}
@@ -797,6 +807,9 @@ protected void processRequest(final Request request, final Link link) {
}
commandsInProgress.incrementAndGet();
try {
+ if (cmd.isReconcile()) {
+ cmd.setRequestSequence(request.getSequence());
+ }
answer = serverResource.executeRequest(cmd);
} finally {
commandsInProgress.decrementAndGet();
@@ -922,7 +935,7 @@ private Answer setupAgentCertificate(final SetupCertificateCommand cmd) {
return new SetupCertificateAnswer(true);
}
- private void processManagementServerList(final List msList, final String lbAlgorithm, final Long lbCheckInterval) {
+ private void processManagementServerList(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval, final boolean triggerHostLB) {
if (CollectionUtils.isNotEmpty(msList) && StringUtils.isNotEmpty(lbAlgorithm)) {
try {
final String newMSHosts = String.format("%s%s%s", com.cloud.utils.StringUtils.toCSVList(msList), IAgentShell.hostLbAlgorithmSeparator, lbAlgorithm);
@@ -934,24 +947,31 @@ private void processManagementServerList(final List msList, final String
throw new CloudRuntimeException("Could not persist received management servers list", e);
}
}
- if ("shuffle".equals(lbAlgorithm)) {
- scheduleHostLBCheckerTask(0);
- } else {
- scheduleHostLBCheckerTask(shell.getLbCheckerInterval(lbCheckInterval));
+ shell.setAvoidHosts(avoidMsList);
+ if (triggerHostLB) {
+ logger.info("Triggering the preferred host checker task now");
+ ScheduledExecutorService hostLbExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("HostLB-Executor"));
+ hostLbExecutor.schedule(new PreferredHostCheckerTask(), 0, TimeUnit.MILLISECONDS);
+ hostLbExecutor.shutdown();
}
+ scheduleHostLBCheckerTask(lbAlgorithm, shell.getLbCheckerInterval(lbCheckInterval));
}
private Answer setupManagementServerList(final SetupMSListCommand cmd) {
- processManagementServerList(cmd.getMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval());
+ processManagementServerList(cmd.getMsList(), cmd.getAvoidMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval(), cmd.getTriggerHostLb());
return new SetupMSListAnswer(true);
}
private Answer migrateAgentToOtherMS(final MigrateAgentConnectionCommand cmd) {
try {
if (CollectionUtils.isNotEmpty(cmd.getMsList())) {
- processManagementServerList(cmd.getMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval());
+ processManagementServerList(cmd.getMsList(), cmd.getAvoidMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval(), false);
}
- migrateAgentConnection(cmd.getAvoidMsList());
+ ScheduledExecutorService migrateAgentConnectionService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("MigrateAgentConnection-Job"));
+ migrateAgentConnectionService.schedule(() -> {
+ migrateAgentConnection(cmd.getAvoidMsList());
+ }, 3, TimeUnit.SECONDS);
+ migrateAgentConnectionService.shutdown();
} catch (Exception e) {
String errMsg = "Migrate agent connection failed, due to " + e.getMessage();
logger.debug(errMsg, e);
@@ -972,25 +992,26 @@ private void migrateAgentConnection(List avoidMsList) {
throw new CloudRuntimeException("No other Management Server hosts to migrate");
}
- String preferredHost = null;
+ String preferredMSHost = null;
for (String msHost : msHostsList) {
try (final Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(msHost, shell.getPort()), 5000);
- preferredHost = msHost;
+ preferredMSHost = msHost;
break;
} catch (final IOException e) {
throw new CloudRuntimeException("Management server host: " + msHost + " is not reachable, to migrate connection");
}
}
- if (preferredHost == null) {
+ if (preferredMSHost == null) {
throw new CloudRuntimeException("Management server host(s) are not reachable, to migrate connection");
}
- logger.debug("Management server host " + preferredHost + " is found to be reachable, trying to reconnect");
+ logger.debug("Management server host " + preferredMSHost + " is found to be reachable, trying to reconnect");
shell.resetHostCounter();
+ shell.setAvoidHosts(avoidMsList);
shell.setConnectionTransfer(true);
- reconnect(link, preferredHost, avoidMsList, true);
+ reconnect(link, preferredMSHost, true);
}
public void processResponse(final Response response, final Link link) {
@@ -1003,12 +1024,21 @@ public void processResponse(final Response response, final Link link) {
for (final IAgentControlListener listener : controlListeners) {
listener.processControlResponse(response, (AgentControlAnswer)answer);
}
- } else if (answer instanceof PingAnswer && (((PingAnswer) answer).isSendStartup()) && reconnectAllowed) {
+ } else if (answer instanceof PingAnswer) {
+ processPingAnswer((PingAnswer) answer);
+ } else {
+ updateLastPingResponseTime();
+ }
+ }
+
+ private void processPingAnswer(final PingAnswer answer) {
+ if ((answer.isSendStartup()) && reconnectAllowed) {
logger.info("Management server requested startup command to reinitialize the agent");
sendStartup(link);
} else {
- updateLastPingResponseTime();
+ serverResource.processPingAnswer((PingAnswer) answer);
}
+ shell.setAvoidHosts(answer.getAvoidMsList());
}
public void processReadyCommand(final Command cmd) {
@@ -1027,7 +1057,7 @@ public void processReadyCommand(final Command cmd) {
}
verifyAgentArch(ready.getArch());
- processManagementServerList(ready.getMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval());
+ processManagementServerList(ready.getMsHostList(), ready.getAvoidMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval(), false);
logger.info("Ready command is processed for agent [id: {}, uuid: {}, name: {}]", getId(), getUuid(), getName());
}
@@ -1073,6 +1103,9 @@ public void processOtherTask(final Task task) {
Answer answer = null;
commandsInProgress.incrementAndGet();
try {
+ if (command.isReconcile()) {
+ command.setRequestSequence(req.getSequence());
+ }
answer = serverResource.executeRequest(command);
} finally {
commandsInProgress.decrementAndGet();
@@ -1289,7 +1322,6 @@ public void doTask(final Task task) throws TaskExecutionException {
processResponse((Response)request, task.getLink());
} else {
//put the requests from mgt server into another thread pool, as the request may take a longer time to finish. Don't block the NIO main thread pool
- //processRequest(request, task.getLink());
requestHandler.submit(new AgentRequestHandler(getType(), getLink(), request));
}
} catch (final ClassNotFoundException e) {
@@ -1299,13 +1331,14 @@ public void doTask(final Task task) throws TaskExecutionException {
}
} else if (task.getType() == Task.Type.DISCONNECT) {
try {
- // an issue has been found if reconnect immediately after disconnecting. please refer to https://github.com/apache/cloudstack/issues/8517
+ // an issue has been found if reconnect immediately after disconnecting.
// wait 5 seconds before reconnecting
+ logger.debug("Wait for 5 secs before reconnecting, disconnect task - {}", () -> getLinkLog(task.getLink()));
Thread.sleep(5000);
} catch (InterruptedException e) {
}
shell.setConnectionTransfer(false);
- logger.debug("Executing disconnect task - {}", () -> getLinkLog(task.getLink()));
+ logger.debug("Executing disconnect task - {} and reconnecting", () -> getLinkLog(task.getLink()));
reconnect(task.getLink());
} else if (task.getType() == Task.Type.OTHER) {
processOtherTask(task);
@@ -1374,26 +1407,26 @@ protected void runInContext() {
if (msList == null || msList.length < 1) {
return;
}
- final String preferredHost = msList[0];
+ final String preferredMSHost = msList[0];
final String connectedHost = shell.getConnectedHost();
logger.debug("Running preferred host checker task, connected host={}, preferred host={}",
- connectedHost, preferredHost);
- if (preferredHost == null || preferredHost.equals(connectedHost) || link == null) {
+ connectedHost, preferredMSHost);
+ if (preferredMSHost == null || preferredMSHost.equals(connectedHost) || link == null) {
return;
}
boolean isHostUp = false;
try (final Socket socket = new Socket()) {
- socket.connect(new InetSocketAddress(preferredHost, shell.getPort()), 5000);
+ socket.connect(new InetSocketAddress(preferredMSHost, shell.getPort()), 5000);
isHostUp = true;
} catch (final IOException e) {
- logger.debug("Host: {} is not reachable", preferredHost);
+ logger.debug("Host: {} is not reachable", preferredMSHost);
}
if (isHostUp && link != null && commandsInProgress.get() == 0) {
if (logger.isDebugEnabled()) {
- logger.debug("Preferred host {} is found to be reachable, trying to reconnect", preferredHost);
+ logger.debug("Preferred host {} is found to be reachable, trying to reconnect", preferredMSHost);
}
shell.resetHostCounter();
- reconnect(link);
+ reconnect(link, preferredMSHost, false);
}
} catch (Throwable t) {
logger.error("Error caught while attempting to connect to preferred host", t);
diff --git a/agent/src/main/java/com/cloud/agent/AgentShell.java b/agent/src/main/java/com/cloud/agent/AgentShell.java
index aea7fd3a8de8..4862e7e001e3 100644
--- a/agent/src/main/java/com/cloud/agent/AgentShell.java
+++ b/agent/src/main/java/com/cloud/agent/AgentShell.java
@@ -66,6 +66,7 @@ public class AgentShell implements IAgentShell, Daemon {
private String _zone;
private String _pod;
private String _host;
+ private List _avoidHosts;
private String _privateIp;
private int _port;
private int _proxyPort;
@@ -76,7 +77,6 @@ public class AgentShell implements IAgentShell, Daemon {
private volatile boolean _exit = false;
private int _pingRetries;
private final List _agents = new ArrayList();
- private String hostToConnect;
private String connectedHost;
private Long preferredHostCheckInterval;
private boolean connectionTransfer = false;
@@ -121,7 +121,7 @@ public String getNextHost() {
if (_hostCounter >= hosts.length) {
_hostCounter = 0;
}
- hostToConnect = hosts[_hostCounter % hosts.length];
+ String hostToConnect = hosts[_hostCounter % hosts.length];
_hostCounter++;
return hostToConnect;
}
@@ -143,11 +143,10 @@ public long getLbCheckerInterval(final Long receivedLbInterval) {
}
@Override
- public void updateConnectedHost() {
- connectedHost = hostToConnect;
+ public void updateConnectedHost(String connectedHost) {
+ this.connectedHost = connectedHost;
}
-
@Override
public void resetHostCounter() {
_hostCounter = 0;
@@ -166,6 +165,16 @@ public void setHosts(final String host) {
}
}
+ @Override
+ public void setAvoidHosts(List avoidHosts) {
+ _avoidHosts = avoidHosts;
+ }
+
+ @Override
+ public List getAvoidHosts() {
+ return _avoidHosts;
+ }
+
@Override
public String getPrivateIp() {
return _privateIp;
diff --git a/agent/src/main/java/com/cloud/agent/IAgentShell.java b/agent/src/main/java/com/cloud/agent/IAgentShell.java
index c0ecd90ae69d..9eefa6d2eeee 100644
--- a/agent/src/main/java/com/cloud/agent/IAgentShell.java
+++ b/agent/src/main/java/com/cloud/agent/IAgentShell.java
@@ -16,6 +16,7 @@
// under the License.
package com.cloud.agent;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -63,9 +64,13 @@ public interface IAgentShell {
String[] getHosts();
+ void setAvoidHosts(List hosts);
+
+ List getAvoidHosts();
+
long getLbCheckerInterval(Long receivedLbInterval);
- void updateConnectedHost();
+ void updateConnectedHost(String connectedHost);
String getConnectedHost();
diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
index 61cd27fff772..e2fe028453f9 100644
--- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
+++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
@@ -117,12 +117,26 @@ public class AgentProperties{
/**
* Local storage path.
- * This property allows multiple values to be entered in a single String. The differente values must be separated by commas.
+ * This property allows multiple values to be entered in a single String. The different values must be separated by commas.
* Data type: String.
* Default value: /var/lib/libvirt/images/
*/
public static final Property LOCAL_STORAGE_PATH = new Property<>("local.storage.path", "/var/lib/libvirt/images/");
+ /**
+ * Enables TLS on the KVM image server transfer endpoint.
+ * Data type: Boolean.
+ * Default value: true
+ */
+ public static final Property IMAGE_SERVER_TLS_ENABLED = new Property<>("image.server.tls.enabled", true);
+
+ /**
+ * The IP address that the KVM image server listens on.
+ * Data type: String.
+ * Default value: null
+ */
+ public static final Property IMAGE_SERVER_LISTEN_ADDRESS = new Property<>("image.server.listen.address", null, String.class);
+
/**
* Directory where Qemu sockets are placed.
* These sockets are for the Qemu Guest Agent and SSVM provisioning.
@@ -134,7 +148,7 @@ public class AgentProperties{
/**
* MANDATORY: The UUID for the local storage pool.
- * This property allows multiple values to be entered in a single String. The differente values must be separated by commas.
+ * This property allows multiple values to be entered in a single String. The different values must be separated by commas.
* Data type: String.
* Default value: null
*/
@@ -155,6 +169,14 @@ public class AgentProperties{
*/
public static final Property CMDS_TIMEOUT = new Property<>("cmds.timeout", 7200);
+ /**
+ * The timeout (in seconds) for the snapshot merge operation, mainly used for classic volume snapshots and disk-only VM snapshots on file-based storage.
+ * This configuration is only considered if libvirt.events.enabled is also true.
+ * Data type: Integer.
+ * Default value: 259200
+ */
+ public static final Property QCOW2_DELTA_MERGE_TIMEOUT = new Property<>("qcow2.delta.merge.timeout", 60 * 60 * 72);
+
/**
* This parameter sets the VM migration speed (in mbps). The default value is -1,
* which means that the agent will try to guess the speed of the guest network and consume all possible bandwidth.
@@ -213,6 +235,15 @@ public class AgentProperties{
*/
public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.script", "libvirt-vm-xml-transformer.groovy");
+ /**
+ * This property is used with the agent.hooks.basedir property to define the Libvirt VM XML transformer shell script.
+ * The shell script is used to execute the Libvirt VM XML transformer script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-xml-transformer.sh
+ */
+ public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.shell_script", "libvirt-vm-xml-transformer.sh");
+
/**
* This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_xml_transformer.script properties to define the Libvirt VM XML transformer method.
* Libvirt XML transformer hook does XML-to-XML transformation.
@@ -233,6 +264,15 @@ public class AgentProperties{
*/
public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.script", "libvirt-vm-state-change.groovy");
+ /**
+ * This property is used with the agent.hooks.basedir property to define the Libvirt VM on start shell script.
+ * The shell script is used to execute the Libvirt VM on start script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh
+ */
+ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.shell_script", "libvirt-vm-state-change.sh");
+
/**
* This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_start.script properties to define the Libvirt VM on start method.
* The hook is called right after Libvirt successfully launched the VM.
@@ -252,6 +292,15 @@ public class AgentProperties{
*/
public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.script", "libvirt-vm-state-change.groovy");
+ /**
+ * This property is used with the agent.hooks.basedir property to define the Libvirt VM on stop shell script.
+ * The shell script is used to execute the Libvirt VM on stop script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh
+ */
+ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.shell_script", "libvirt-vm-state-change.sh");
+
/**
* This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_stop.script properties to define the Libvirt VM on stop method.
* The hook is called right after libvirt successfully stopped the VM.
@@ -269,6 +318,15 @@ public class AgentProperties{
*/
public static final Property NETWORK_BRIDGE_TYPE = new Property<>("network.bridge.type", "native");
+ /**
+ * Sets the VXLAN networking mode used by the BridgeVifDriver.
+ * Possible values: multicast | evpn
+ * When set to evpn, the driver will use modifyvxlan-evpn.sh instead of modifyvxlan.sh.
+ * Data type: String.
+ * Default value: multicast
+ */
+ public static final Property NETWORK_VXLAN_MODE = new Property<>("network.vxlan.mode", "multicast");
+
/**
* Sets the driver used to plug and unplug NICs from the bridges.
* A sensible default value will be selected based on the network.bridge.type but can be overridden here.
@@ -390,8 +448,9 @@ public class AgentProperties{
public static final Property GUEST_CPU_ARCH = new Property<>("guest.cpu.arch", null, String.class);
/**
- * This param will require CPU features on the CPU section.
- * The features listed in this property must be separated by a blank space (see example below).
+ * Specifies required CPU features for end-user and system VMs.
+ * These features must be present on the host CPU for VM deployment.
+ * Multiple features should be separated by whitespace (see example below).
* Possible values: vmx vme
* Data type: String.
* Default value: null
@@ -557,10 +616,10 @@ public class AgentProperties{
/**
* This parameter specifies if the host must be rebooted when something goes wrong with the heartbeat.
* Data type: Boolean.
- * Default value: true
+ * Default value: false
*/
public static final Property REBOOT_HOST_AND_ALERT_MANAGEMENT_ON_HEARTBEAT_TIMEOUT
- = new Property<>("reboot.host.and.alert.management.on.heartbeat.timeout", true);
+ = new Property<>("reboot.host.and.alert.management.on.heartbeat.timeout", false);
/**
* Enables manually setting CPU's topology on KVM's VM.
@@ -758,6 +817,44 @@ public Property getWorkers() {
*/
public static final Property VIRTV2V_VERBOSE_ENABLED = new Property<>("virtv2v.verbose.enabled", false);
+ /**
+ * Set env TMPDIR var for virt-v2v Instance Conversion from VMware to KVM
+ * Data type: String.
+ * Default value: null
+ */
+ public static final Property CONVERT_ENV_TMPDIR = new Property<>("convert.instance.env.tmpdir", null, String.class);
+
+ /**
+ * Set env VIRT_V2V_TMPDIR var for virt-v2v Instance Conversion from VMware to KVM
+ * Data type: String.
+ * Default value: null
+ */
+ public static final Property CONVERT_ENV_VIRTV2V_TMPDIR = new Property<>("convert.instance.env.virtv2v.tmpdir", null, String.class);
+
+ /**
+ * Path to the VDDK library directory on the KVM conversion host, used when converting VMs from VMware to KVM via VDDK.
+ * This directory is passed to virt-v2v as -io vddk-libdir=<path>.
+ * Data type: String.
+ * Default value: null
+ */
+ public static final Property VDDK_LIB_DIR = new Property<>("vddk.lib.dir", null, String.class);
+
+ /**
+ * Ordered list of VDDK transports for virt-v2v, passed as -io vddk-transports=<value>.
+ * Example: nbd:nbdssl.
+ * Data type: String.
+ * Default value: null
+ */
+ public static final Property VDDK_TRANSPORTS = new Property<>("vddk.transports", null, String.class);
+
+ /**
+ * vCenter TLS certificate thumbprint used by virt-v2v VDDK mode, passed as -io vddk-thumbprint=<value>.
+ * If unset, the KVM host computes it at runtime from the vCenter endpoint.
+ * Data type: String.
+ * Default value: null
+ */
+ public static final Property VDDK_THUMBPRINT = new Property<>("vddk.thumbprint", null, String.class);
+
/**
* BGP controll CIDR
* Data type: String.
@@ -816,14 +913,37 @@ public Property getWorkers() {
* Data type: Integer.
* Default value: null
*/
- public static final Property SSL_HANDSHAKE_TIMEOUT = new Property<>("ssl.handshake.timeout", null, Integer.class);
+ public static final Property SSL_HANDSHAKE_TIMEOUT = new Property<>("ssl.handshake.timeout", 30, Integer.class);
+
+ /**
+ * Timeout (in seconds) to wait for the incremental snapshot to complete.
+ * */
+ public static final Property INCREMENTAL_SNAPSHOT_TIMEOUT = new Property<>("incremental.snapshot.timeout", 10800);
+
+ /**
+ * Timeout (in seconds) to wait for the snapshot reversion to complete.
+ * */
+ public static final Property REVERT_SNAPSHOT_TIMEOUT = new Property<>("revert.snapshot.timeout", 10800);
+
+ /**
+ * If set to true, creates VMs as full clones of their templates on KVM hypervisor. Creates as linked clones otherwise.
+ * Data type: Boolean.
+ * Default value: false
+ */
+ public static final Property CREATE_FULL_CLONE = new Property<>("create.full.clone", false);
+
+ /**
+ * Time, in seconds, to wait before retrying to rebase during the incremental snapshot process.
+ * */
+ public static final Property INCREMENTAL_SNAPSHOT_RETRY_REBASE_WAIT = new Property<>("incremental.snapshot.retry.rebase.wait", 60);
+
public static class Property {
private String name;
private T defaultValue;
private Class typeClass;
- Property(String name, T value) {
+ public Property(String name, T value) {
init(name, value);
}
diff --git a/agent/src/main/java/com/cloud/agent/resource/DummyResource.java b/agent/src/main/java/com/cloud/agent/resource/DummyResource.java
index fe519ca9497f..4002e53b5858 100644
--- a/agent/src/main/java/com/cloud/agent/resource/DummyResource.java
+++ b/agent/src/main/java/com/cloud/agent/resource/DummyResource.java
@@ -20,7 +20,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.UUID;
import com.cloud.agent.IAgentControl;
import com.cloud.agent.api.Answer;
@@ -40,6 +39,7 @@
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.utils.StringUtils;
+import com.cloud.utils.UuidUtils;
public class DummyResource implements ServerResource {
String _name;
@@ -133,7 +133,7 @@ protected StoragePoolInfo initializeLocalStorage() {
String hostIp = getConfiguredProperty("private.ip.address", "127.0.0.1");
String localStoragePath = getConfiguredProperty("local.storage.path", "/mnt");
String lh = hostIp + localStoragePath;
- String uuid = UUID.nameUUIDFromBytes(lh.getBytes(StringUtils.getPreferredCharset())).toString();
+ String uuid = UuidUtils.nameUUIDFromBytes(lh.getBytes(StringUtils.getPreferredCharset())).toString();
String capacity = getConfiguredProperty("local.storage.capacity", "1000000000");
String available = getConfiguredProperty("local.storage.avail", "10000000");
diff --git a/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
index 26f9d4b3d732..ef98fa532ecb 100644
--- a/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
+++ b/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
@@ -175,12 +175,12 @@ private Answer executeProxyLoadScan(final Command cmd, final long proxyVmId, fin
try {
is.close();
} catch (final IOException e) {
- logger.warn("Exception when closing , console proxy address : {}", proxyManagementIp);
+ logger.warn("Exception when closing , console proxy address: {}", proxyManagementIp);
success = false;
}
}
} catch (final IOException e) {
- logger.warn("Unable to open console proxy command port url, console proxy address : {}", proxyManagementIp);
+ logger.warn("Unable to open console proxy command port url, console proxy address: {}", proxyManagementIp);
success = false;
}
@@ -331,7 +331,7 @@ private void launchConsoleProxy(final byte[] ksBits, final String ksPassword, fi
final Object resource = this;
logger.info("Building class loader for com.cloud.consoleproxy.ConsoleProxy");
if (consoleProxyMain == null) {
- logger.info("Running com.cloud.consoleproxy.ConsoleProxy with encryptor password={}", encryptorPassword);
+ logger.info("Running com.cloud.consoleproxy.ConsoleProxy");
consoleProxyMain = new Thread(new ManagedContextRunnable() {
@Override
protected void runInContext() {
diff --git a/agent/src/test/java/com/cloud/agent/AgentShellTest.java b/agent/src/test/java/com/cloud/agent/AgentShellTest.java
index 6d9758cc3dc8..d8def24a603a 100644
--- a/agent/src/test/java/com/cloud/agent/AgentShellTest.java
+++ b/agent/src/test/java/com/cloud/agent/AgentShellTest.java
@@ -358,7 +358,7 @@ public void updateAndGetConnectedHost() {
AgentShell shell = new AgentShell();
shell.setHosts("test");
shell.getNextHost();
- shell.updateConnectedHost();
+ shell.updateConnectedHost("test");
Assert.assertEquals(expected, shell.getConnectedHost());
}
diff --git a/api/pom.xml b/api/pom.xml
index ec68e24c7e59..4cdb57b6414c 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -24,7 +24,7 @@
org.apache.cloudstack
cloudstack
- 4.21.0.0-SNAPSHOT
+ 4.23.0.0-SNAPSHOT
@@ -71,6 +71,11 @@
cloud-framework-direct-download
${project.version}
+
+ org.apache.cloudstack
+ cloud-framework-kms
+ ${project.version}
+
diff --git a/api/src/main/java/com/cloud/agent/api/Command.java b/api/src/main/java/com/cloud/agent/api/Command.java
index eb979c0060b9..c4e99cb41707 100644
--- a/api/src/main/java/com/cloud/agent/api/Command.java
+++ b/api/src/main/java/com/cloud/agent/api/Command.java
@@ -19,9 +19,10 @@
import java.util.HashMap;
import java.util.Map;
-import com.cloud.agent.api.LogLevel.Log4jLevel;
-import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import com.cloud.agent.api.LogLevel.Log4jLevel;
/**
* implemented by classes that extends the Command class. Command specifies
@@ -35,6 +36,23 @@ public static enum OnError {
Continue, Stop
}
+ public enum State {
+ CREATED, // Command is created by management server
+ STARTED, // Command is started by agent
+ PROCESSING, // Processing by agent
+ PROCESSING_IN_BACKEND, // Processing in backend by agent
+ COMPLETED, // Operation succeeds by agent or management server
+ FAILED, // Operation fails by agent
+ RECONCILE_RETRY, // Ready for retry of reconciliation
+ RECONCILING, // Being reconciled by management server
+ RECONCILED, // Reconciled by management server
+ RECONCILE_SKIPPED, // Skip the reconciliation as the resource state is inconsistent with the command
+ RECONCILE_FAILED, // Fail to reconcile by management server
+ TIMED_OUT, // Timed out on management server or agent
+ INTERRUPTED, // Interrupted by management server or agent (for example agent is restarted),
+ DANGLED_IN_BACKEND // Backend process which cannot be processed normally (for example agent is restarted)
+ }
+
public static final String HYPERVISOR_TYPE = "hypervisorType";
// allow command to carry over hypervisor or other environment related context info
@@ -42,6 +60,8 @@ public static enum OnError {
protected Map contextMap = new HashMap();
private int wait; //in second
private boolean bypassHostMaintenance = false;
+ private transient long requestSequence = 0L;
+ protected Map> externalDetails;
protected Command() {
this.wait = 0;
@@ -82,6 +102,10 @@ public String getContextParam(String name) {
return contextMap.get(name);
}
+ public Map getContextMap() {
+ return contextMap;
+ }
+
public boolean allowCaching() {
return true;
}
@@ -94,6 +118,26 @@ public void setBypassHostMaintenance(boolean bypassHostMaintenance) {
this.bypassHostMaintenance = bypassHostMaintenance;
}
+ public boolean isReconcile() {
+ return false;
+ }
+
+ public long getRequestSequence() {
+ return requestSequence;
+ }
+
+ public void setRequestSequence(long requestSequence) {
+ this.requestSequence = requestSequence;
+ }
+
+ public void setExternalDetails(Map> externalDetails) {
+ this.externalDetails = externalDetails;
+ }
+
+ public Map> getExternalDetails() {
+ return externalDetails;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java
index 85ffc1898209..5515a9c48bcb 100644
--- a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java
+++ b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java
@@ -15,10 +15,24 @@
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
+
+import org.apache.cloudstack.gpu.GpuDevice;
+
public class VgpuTypesInfo {
+ private boolean passthroughEnabled = true;
+ private GpuDevice.DeviceType deviceType;
+ private String parentBusAddress;
+ private String busAddress;
+ private String numaNode;
+ private String pciRoot;
+ private String deviceId;
+ private String deviceName;
+ private String vendorId;
+ private String vendorName;
private String modelName;
private String groupName;
+ private String vmName;
private Long maxHeads;
private Long videoRam;
private Long maxResolutionX;
@@ -26,6 +40,7 @@ public class VgpuTypesInfo {
private Long maxVgpuPerGpu;
private Long remainingCapacity;
private Long maxCapacity;
+ private boolean display = false;
public String getModelName() {
return modelName;
@@ -39,22 +54,42 @@ public Long getVideoRam() {
return videoRam;
}
+ public void setVideoRam(Long videoRam) {
+ this.videoRam = videoRam;
+ }
+
public Long getMaxHeads() {
return maxHeads;
}
+ public void setMaxHeads(Long maxHeads) {
+ this.maxHeads = maxHeads;
+ }
+
public Long getMaxResolutionX() {
return maxResolutionX;
}
+ public void setMaxResolutionX(Long maxResolutionX) {
+ this.maxResolutionX = maxResolutionX;
+ }
+
public Long getMaxResolutionY() {
return maxResolutionY;
}
+ public void setMaxResolutionY(Long maxResolutionY) {
+ this.maxResolutionY = maxResolutionY;
+ }
+
public Long getMaxVpuPerGpu() {
return maxVgpuPerGpu;
}
+ public void setMaxVgpuPerGpu(Long maxVgpuPerGpu) {
+ this.maxVgpuPerGpu = maxVgpuPerGpu;
+ }
+
public Long getRemainingCapacity() {
return remainingCapacity;
}
@@ -71,8 +106,133 @@ public void setMaxVmCapacity(Long maxCapacity) {
this.maxCapacity = maxCapacity;
}
- public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY, Long maxVgpuPerGpu,
- Long remainingCapacity, Long maxCapacity) {
+ public boolean isPassthroughEnabled() {
+ return passthroughEnabled;
+ }
+
+ public void setPassthroughEnabled(boolean passthroughEnabled) {
+ this.passthroughEnabled = passthroughEnabled;
+ }
+
+ public GpuDevice.DeviceType getDeviceType() {
+ return deviceType;
+ }
+
+ public void setDeviceType(GpuDevice.DeviceType deviceType) {
+ this.deviceType = deviceType;
+ }
+
+ public String getParentBusAddress() {
+ return parentBusAddress;
+ }
+
+ public void setParentBusAddress(String parentBusAddress) {
+ this.parentBusAddress = parentBusAddress;
+ }
+
+ public String getBusAddress() {
+ return busAddress;
+ }
+
+ public void setBusAddress(String busAddress) {
+ this.busAddress = busAddress;
+ }
+
+ public String getNumaNode() {
+ return numaNode;
+ }
+
+ public void setNumaNode(String numaNode) {
+ this.numaNode = numaNode;
+ }
+
+ public String getPciRoot() {
+ return pciRoot;
+ }
+
+ public void setPciRoot(String pciRoot) {
+ this.pciRoot = pciRoot;
+ }
+
+ public String getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceId(String deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ public void setDeviceName(String deviceName) {
+ this.deviceName = deviceName;
+ }
+
+ public String getVendorId() {
+ return vendorId;
+ }
+
+ public void setVendorId(String vendorId) {
+ this.vendorId = vendorId;
+ }
+
+ public String getVendorName() {
+ return vendorName;
+ }
+
+ public void setVendorName(String vendorName) {
+ this.vendorName = vendorName;
+ }
+
+ public String getVmName() {
+ return vmName;
+ }
+
+ public void setVmName(String vmName) {
+ this.vmName = vmName;
+ }
+
+ public boolean isDisplay() {
+ return display;
+ }
+
+ public void setDisplay(boolean display) {
+ this.display = display;
+ }
+
+ public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress,
+ String vendorId, String vendorName, String deviceId, String deviceName, String numaNode, String pciRoot
+ ) {
+ this.deviceType = deviceType;
+ this.groupName = groupName;
+ this.modelName = modelName;
+ this.busAddress = busAddress;
+ this.deviceId = deviceId;
+ this.deviceName = deviceName;
+ this.vendorId = vendorId;
+ this.vendorName = vendorName;
+ this.numaNode = numaNode;
+ this.pciRoot = pciRoot;
+ }
+
+ public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress,
+ String vendorId, String vendorName, String deviceId, String deviceName
+ ) {
+ this.deviceType = deviceType;
+ this.groupName = groupName;
+ this.modelName = modelName;
+ this.busAddress = busAddress;
+ this.deviceId = deviceId;
+ this.deviceName = deviceName;
+ this.vendorId = vendorId;
+ this.vendorName = vendorName;
+ }
+
+ public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX,
+ Long maxResolutionY, Long maxVgpuPerGpu, Long remainingCapacity, Long maxCapacity
+ ) {
this.groupName = groupName;
this.modelName = modelName;
this.videoRam = videoRam;
diff --git a/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java b/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java
index 6396e3deb723..23167c5c53b0 100644
--- a/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java
+++ b/api/src/main/java/com/cloud/agent/api/storage/OVFHelper.java
@@ -119,8 +119,7 @@ protected OVFPropertyTO createOVFPropertyFromNode(Node node, int index, String c
boolean password = StringUtils.isNotBlank(passStr) && passStr.equalsIgnoreCase("true");
String label = ovfParser.getChildNodeValue(node, "Label");
String description = ovfParser.getChildNodeValue(node, "Description");
- logger.debug("Creating OVF property index " + index + (category == null ? "" : " for category " + category)
- + " with key = " + key);
+ logger.debug("Creating OVF property index {} {} with key = {}", index, (category == null ? "" : " for category " + category), key);
return new OVFPropertyTO(key, type, value, qualifiers, userConfigurable,
label, description, password, index, category);
}
@@ -152,7 +151,7 @@ public List getConfigurableOVFPropertiesFromDocument(Document doc
if (child.getNodeName().equalsIgnoreCase("Category") ||
child.getNodeName().endsWith(":Category")) {
lastCategoryFound = child.getTextContent();
- logger.info("Category found " + lastCategoryFound);
+ logger.info("Category found {}", lastCategoryFound);
} else if (child.getNodeName().equalsIgnoreCase("Property") ||
child.getNodeName().endsWith(":Property")) {
OVFPropertyTO prop = createOVFPropertyFromNode(child, propertyIndex, lastCategoryFound);
@@ -250,13 +249,13 @@ private List matchHardwareItemsToDiskAndFilesInformation(List extractDisksFromOvfDocumentTree(Document doc) {
od._controller = getControllerType(items, od._diskId);
vd.add(od);
}
- if (logger.isTraceEnabled()) {
- logger.trace(String.format("found %d disk definitions",vd.size()));
- }
+ logger.trace("Found {} disk definitions", vd.size());
return vd;
}
@@ -366,9 +363,7 @@ protected List extractFilesFromOvfDocumentTree(File ovfFile, Document d
vf.add(of);
}
}
- if (logger.isTraceEnabled()) {
- logger.trace(String.format("found %d file definitions in %s",vf.size(), ovfFile.getPath()));
- }
+ logger.trace("Found {} file definitions in {}", vf.size(), ovfFile.getPath());
return vf;
}
@@ -506,7 +501,7 @@ private void writeDocumentToFile(String newOvfFilePath, Document doc) {
outfile.write(writer.toString());
outfile.close();
} catch (IOException | TransformerException e) {
- logger.info("Unexpected exception caught while rewriting OVF:" + e.getMessage(), e);
+ logger.info("Unexpected exception caught while rewriting OVF: {}", e.getMessage(), e);
throw new CloudRuntimeException(e);
}
}
@@ -522,9 +517,7 @@ OVFFile getFileDefinitionFromDiskDefinition(String fileRef, List files)
public List getNetPrerequisitesFromDocument(Document doc) throws InternalErrorException {
if (doc == null) {
- if (logger.isTraceEnabled()) {
- logger.trace("no document to parse; returning no prerequisite networks");
- }
+ logger.trace("No document to parse; returning no prerequisite networks");
return Collections.emptyList();
}
@@ -540,9 +533,7 @@ public List getNetPrerequisitesFromDocument(Document doc) throws I
private void matchNicsToNets(Map nets, Node systemElement) {
final DocumentTraversal traversal = (DocumentTraversal) systemElement;
final NodeIterator iterator = traversal.createNodeIterator(systemElement, NodeFilter.SHOW_ELEMENT, null, true);
- if (logger.isTraceEnabled()) {
- logger.trace(String.format("starting out with %d network-prerequisites, parsing hardware",nets.size()));
- }
+ logger.trace("Starting out with {} network-prerequisites, parsing hardware", nets.size());
int nicCount = 0;
for (Node n = iterator.nextNode(); n != null; n = iterator.nextNode()) {
final Element e = (Element) n;
@@ -550,9 +541,7 @@ private void matchNicsToNets(Map nets, Node systemElement)
nicCount++;
String name = e.getTextContent(); // should be in our nets
if(nets.get(name) == null) {
- if(logger.isInfoEnabled()) {
- logger.info(String.format("found a nic definition without a network definition byname %s, adding it to the list.", name));
- }
+ logger.info("Found a NIC definition without a Network definition by name {}, adding it to the list.", name);
nets.put(name, new OVFNetworkTO());
}
OVFNetworkTO thisNet = nets.get(name);
@@ -561,9 +550,7 @@ private void matchNicsToNets(Map nets, Node systemElement)
}
}
}
- if (logger.isTraceEnabled()) {
- logger.trace(String.format("ending up with %d network-prerequisites, parsed %d nics", nets.size(), nicCount));
- }
+ logger.trace("Ending up with {} network-prerequisites, parsed {} nics", nets.size(), nicCount);
}
/**
@@ -585,7 +572,7 @@ private void fillNicPrerequisites(OVFNetworkTO nic, Node parentNode) {
int addressOnParent = Integer.parseInt(addressOnParentStr);
nic.setAddressOnParent(addressOnParent);
} catch (NumberFormatException e) {
- logger.warn("Encountered element of type \"AddressOnParent\", that could not be parse to an integer number: " + addressOnParentStr);
+ logger.warn("Encountered element of type \"AddressOnParent\", that could not be parse to an integer number: {}", addressOnParentStr);
}
boolean automaticAllocation = StringUtils.isNotBlank(automaticAllocationStr) && Boolean.parseBoolean(automaticAllocationStr);
@@ -597,7 +584,7 @@ private void fillNicPrerequisites(OVFNetworkTO nic, Node parentNode) {
int instanceId = Integer.parseInt(instanceIdStr);
nic.setInstanceID(instanceId);
} catch (NumberFormatException e) {
- logger.warn("Encountered element of type \"InstanceID\", that could not be parse to an integer number: " + instanceIdStr);
+ logger.warn("Encountered element of type \"InstanceID\", that could not be parse to an integer number: {}", instanceIdStr);
}
nic.setResourceSubType(resourceSubType);
@@ -630,9 +617,7 @@ private Map getNetworksFromDocumentTree(Document doc) {
nets.put(networkName,network);
}
- if (logger.isTraceEnabled()) {
- logger.trace(String.format("found %d networks in template", nets.size()));
- }
+ logger.trace("Found {} Networks in Template", nets.size());
return nets;
}
@@ -771,7 +756,7 @@ private Long getLongValueFromString(String value) {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
- logger.debug("Could not parse the value: " + value + ", ignoring it");
+ logger.debug("Could not parse the value: {}, ignoring it", value);
}
}
return null;
@@ -782,7 +767,7 @@ private Integer getIntValueFromString(String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
- logger.debug("Could not parse the value: " + value + ", ignoring it");
+ logger.debug("Could not parse the value: {}, ignoring it", value);
}
}
return null;
@@ -820,7 +805,7 @@ public List getEulaSectionsFromDocument(Document doc) {
try {
compressedLicense = compressOVFEula(eulaLicense);
} catch (IOException e) {
- logger.error("Could not compress the license for info " + eulaInfo);
+ logger.error("Could not compress the license for info {}", eulaInfo);
continue;
}
OVFEulaSectionTO eula = new OVFEulaSectionTO(eulaInfo, compressedLicense, eulaIndex);
diff --git a/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java b/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java
index 38f478d63cf8..316ab4ea87b9 100644
--- a/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java
+++ b/api/src/main/java/com/cloud/agent/api/storage/OVFParser.java
@@ -54,7 +54,7 @@ public OVFParser() {
documentBuilderFactory.setNamespaceAware(true);
documentBuilder = documentBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
- logger.error("Cannot start the OVF parser: " + e.getMessage(), e);
+ logger.error("Cannot start the OVF parser: {}", e.getMessage(), e);
}
}
@@ -70,7 +70,7 @@ public Document parseOVFFile(String ovfFilePath) {
try {
return documentBuilder.parse(new File(ovfFilePath));
} catch (SAXException | IOException e) {
- logger.error("Error parsing " + ovfFilePath + " " + e.getMessage(), e);
+ logger.error("Error parsing {} {}", ovfFilePath, e.getMessage(), e);
return null;
}
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/BucketTO.java b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java
index f7e4bfea80fb..fd8237998a74 100644
--- a/api/src/main/java/com/cloud/agent/api/to/BucketTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java
@@ -26,10 +26,13 @@ public final class BucketTO {
private String secretKey;
+ private long accountId;
+
public BucketTO(Bucket bucket) {
this.name = bucket.getName();
this.accessKey = bucket.getAccessKey();
this.secretKey = bucket.getSecretKey();
+ this.accountId = bucket.getAccountId();
}
public BucketTO(String name) {
@@ -47,4 +50,8 @@ public String getAccessKey() {
public String getSecretKey() {
return this.secretKey;
}
+
+ public long getAccountId() {
+ return this.accountId;
+ }
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java
index d22df2df172e..5664de790919 100644
--- a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java
@@ -46,7 +46,7 @@ public class DiskTO {
private Long diskSeq;
private String path;
private Volume.Type type;
- private Map _details;
+ private Map details;
public DiskTO() {
@@ -92,10 +92,10 @@ public void setType(Volume.Type type) {
}
public void setDetails(Map details) {
- _details = details;
+ this.details = details;
}
public Map getDetails() {
- return _details;
+ return details;
}
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java
index 25c75001a3c1..69350815be3a 100644
--- a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java
@@ -47,7 +47,7 @@ public class FirewallRuleTO implements InternalIdentity {
int[] srcPortRange;
boolean revoked;
boolean alreadyAdded;
- private List sourceCidrList;
+ protected List sourceCidrList;
private List destCidrList;
FirewallRule.Purpose purpose;
private Integer icmpType;
diff --git a/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java b/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java
index 4afe080477b7..6e9cee06dd38 100644
--- a/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java
@@ -16,7 +16,9 @@
// under the License.
package com.cloud.agent.api.to;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import com.cloud.agent.api.VgpuTypesInfo;
@@ -24,9 +26,23 @@ public class GPUDeviceTO {
private String gpuGroup;
private String vgpuType;
+ private int gpuCount;
private HashMap> groupDetails = new HashMap>();
+ private List gpuDevices = new ArrayList<>();
- public GPUDeviceTO( String gpuGroup, String vgpuType, HashMap> groupDetails) {
+ public GPUDeviceTO(String gpuGroup, String vgpuType, int gpuCount,
+ HashMap> groupDetails,
+ List gpuDevices) {
+ this.gpuGroup = gpuGroup;
+ this.vgpuType = vgpuType;
+ this.groupDetails = groupDetails;
+ this.gpuCount = gpuCount;
+ this.gpuDevices = gpuDevices;
+
+ }
+
+ public GPUDeviceTO(String gpuGroup, String vgpuType,
+ HashMap> groupDetails) {
this.gpuGroup = gpuGroup;
this.vgpuType = vgpuType;
this.groupDetails = groupDetails;
@@ -48,6 +64,14 @@ public void setVgpuType(String vgpuType) {
this.vgpuType = vgpuType;
}
+ public int getGpuCount() {
+ return gpuCount;
+ }
+
+ public void setGpuCount(int gpuCount) {
+ this.gpuCount = gpuCount;
+ }
+
public HashMap> getGroupDetails() {
return groupDetails;
}
@@ -56,4 +80,11 @@ public void setGroupDetails(HashMap> grou
this.groupDetails = groupDetails;
}
+ public List getGpuDevices() {
+ return gpuDevices;
+ }
+
+ public void setGpuDevices(List gpuDevices) {
+ this.gpuDevices = gpuDevices;
+ }
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java b/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java
index f395f26aeed6..6c4b9e607c51 100644
--- a/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java
@@ -71,7 +71,7 @@ public LoadBalancerTO(String uuid, String srcIp, int srcPort, String protocol, S
this.destinations = new DestinationTO[destinations.size()];
this.stickinessPolicies = null;
this.sslCert = null;
- this.lbProtocol = null;
+ this.lbProtocol = protocol;
int i = 0;
for (LbDestination destination : destinations) {
this.destinations[i++] = new DestinationTO(destination.getIpAddress(), destination.getDestinationPortStart(), destination.isRevoked(), false);
@@ -205,6 +205,10 @@ public LbSslCert getSslCert() {
return this.sslCert;
}
+ public void setLbSslCert(LbSslCert sslCert) {
+ this.sslCert = sslCert;
+ }
+
public String getSrcIpVlan() {
return srcIpVlan;
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java
index bd08ce811013..d65ec0e3daad 100644
--- a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java
@@ -36,7 +36,7 @@ public class NetworkTO {
protected TrafficType type;
protected URI broadcastUri;
protected URI isolationUri;
- protected boolean isSecurityGroupEnabled;
+ protected boolean securityGroupEnabled;
protected String name;
protected String ip6address;
protected String ip6gateway;
@@ -112,7 +112,7 @@ public String getName() {
}
public void setSecurityGroupEnabled(boolean enabled) {
- this.isSecurityGroupEnabled = enabled;
+ this.securityGroupEnabled = enabled;
}
/**
@@ -221,7 +221,7 @@ public void setIsolationuri(URI isolationUri) {
}
public boolean isSecurityGroupEnabled() {
- return this.isSecurityGroupEnabled;
+ return this.securityGroupEnabled;
}
public void setIp6Dns1(String ip6Dns1) {
diff --git a/api/src/main/java/com/cloud/agent/api/to/NicTO.java b/api/src/main/java/com/cloud/agent/api/to/NicTO.java
index 573363c04fb1..2ed7d9f9a201 100644
--- a/api/src/main/java/com/cloud/agent/api/to/NicTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/NicTO.java
@@ -33,6 +33,7 @@ public class NicTO extends NetworkTO {
boolean dpdkEnabled;
Integer mtu;
Long networkId;
+ boolean enabled;
String networkSegmentName;
@@ -86,6 +87,14 @@ public void setUuid(String uuid) {
this.nicUuid = uuid;
}
+ public String getNicUuid() {
+ return nicUuid;
+ }
+
+ public void setNicUuid(String nicUuid) {
+ this.nicUuid = nicUuid;
+ }
+
@Override
public String toString() {
return new StringBuilder("[Nic:").append(type).append("-").append(ip).append("-").append(broadcastUri).append("]").toString();
@@ -146,4 +155,12 @@ public String getNetworkSegmentName() {
public void setNetworkSegmentName(String networkSegmentName) {
this.networkSegmentName = networkSegmentName;
}
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java
index d43625c09a92..91f337c5f55b 100644
--- a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java
@@ -21,8 +21,6 @@
import com.cloud.utils.net.NetUtils;
import org.apache.commons.lang3.StringUtils;
-import java.util.List;
-
/**
* PortForwardingRuleTO specifies one port forwarding rule.
*
@@ -32,8 +30,6 @@ public class PortForwardingRuleTO extends FirewallRuleTO {
String dstIp;
int[] dstPortRange;
- List sourceCidrList;
-
protected PortForwardingRuleTO() {
super();
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java
index d86eb2a3a7f7..7daeb9649177 100644
--- a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java
@@ -27,6 +27,7 @@ public class RemoteInstanceTO implements Serializable {
private Hypervisor.HypervisorType hypervisorType;
private String instanceName;
+ private String instancePath;
// VMware Remote Instances parameters (required for exporting OVA through ovftool)
// TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes
@@ -35,24 +36,35 @@ public class RemoteInstanceTO implements Serializable {
private String vcenterPassword;
private String vcenterHost;
private String datacenterName;
+ private String clusterName;
+ private String hostName;
public RemoteInstanceTO() {
}
- public RemoteInstanceTO(String instanceName) {
+ public RemoteInstanceTO(String instanceName, String clusterName, String hostName) {
this.hypervisorType = Hypervisor.HypervisorType.VMware;
this.instanceName = instanceName;
+ this.clusterName = clusterName;
+ this.hostName = hostName;
}
- public RemoteInstanceTO(String instanceName, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
+ public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
this.hypervisorType = Hypervisor.HypervisorType.VMware;
this.instanceName = instanceName;
+ this.instancePath = instancePath;
this.vcenterHost = vcenterHost;
this.vcenterUsername = vcenterUsername;
this.vcenterPassword = vcenterPassword;
this.datacenterName = datacenterName;
}
+ public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName, String clusterName, String hostName) {
+ this(instanceName, instancePath, vcenterHost, vcenterUsername, vcenterPassword, datacenterName);
+ this.clusterName = clusterName;
+ this.hostName = hostName;
+ }
+
public Hypervisor.HypervisorType getHypervisorType() {
return this.hypervisorType;
}
@@ -61,6 +73,10 @@ public String getInstanceName() {
return this.instanceName;
}
+ public String getInstancePath() {
+ return this.instancePath;
+ }
+
public String getVcenterUsername() {
return vcenterUsername;
}
@@ -76,4 +92,12 @@ public String getVcenterHost() {
public String getDatacenterName() {
return datacenterName;
}
+
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public String getHostName() {
+ return hostName;
+ }
}
diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java
new file mode 100644
index 000000000000..5b22afdedd53
--- /dev/null
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineMetadataTO.java
@@ -0,0 +1,182 @@
+// 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 com.cloud.agent.api.to;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class VirtualMachineMetadataTO {
+ // VM details
+ private final String name;
+ private final String internalName;
+ private final String displayName;
+ private final String instanceUuid;
+ private final Integer cpuCores;
+ private final Integer memory;
+ private final Long created;
+ private final Long started;
+
+ // Owner details
+ private final String ownerDomainUuid;
+ private final String ownerDomainName;
+ private final String ownerAccountUuid;
+ private final String ownerAccountName;
+ private final String ownerProjectUuid;
+ private final String ownerProjectName;
+
+ // Host and service offering
+ private final String serviceOfferingName;
+ private final List serviceOfferingHostTags;
+
+ // zone, pod, and cluster details
+ private final String zoneName;
+ private final String zoneUuid;
+ private final String podName;
+ private final String podUuid;
+ private final String clusterName;
+ private final String clusterUuid;
+
+ // resource tags
+ private final Map resourceTags;
+
+ public VirtualMachineMetadataTO(
+ String name, String internalName, String displayName, String instanceUuid, Integer cpuCores, Integer memory, Long created, Long started,
+ String ownerDomainUuid, String ownerDomainName, String ownerAccountUuid, String ownerAccountName, String ownerProjectUuid, String ownerProjectName,
+ String serviceOfferingName, List serviceOfferingHostTags,
+ String zoneName, String zoneUuid, String podName, String podUuid, String clusterName, String clusterUuid, Map resourceTags) {
+ /*
+ * Something failed in the metadata shall not be a fatal error, the VM can still be started
+ * Thus, the unknown fields just get an explicit "unknown" value so it can be fixed in case
+ * there are bugs on some execution paths.
+ * */
+
+ this.name = (name != null) ? name : "unknown";
+ this.internalName = (internalName != null) ? internalName : "unknown";
+ this.displayName = (displayName != null) ? displayName : "unknown";
+ this.instanceUuid = (instanceUuid != null) ? instanceUuid : "unknown";
+ this.cpuCores = (cpuCores != null) ? cpuCores : -1;
+ this.memory = (memory != null) ? memory : -1;
+ this.created = (created != null) ? created : 0;
+ this.started = (started != null) ? started : 0;
+ this.ownerDomainUuid = (ownerDomainUuid != null) ? ownerDomainUuid : "unknown";
+ this.ownerDomainName = (ownerDomainName != null) ? ownerDomainName : "unknown";
+ this.ownerAccountUuid = (ownerAccountUuid != null) ? ownerAccountUuid : "unknown";
+ this.ownerAccountName = (ownerAccountName != null) ? ownerAccountName : "unknown";
+ this.ownerProjectUuid = (ownerProjectUuid != null) ? ownerProjectUuid : "unknown";
+ this.ownerProjectName = (ownerProjectName != null) ? ownerProjectName : "unknown";
+ this.serviceOfferingName = (serviceOfferingName != null) ? serviceOfferingName : "unknown";
+ this.serviceOfferingHostTags = (serviceOfferingHostTags != null) ? serviceOfferingHostTags : new ArrayList<>();
+ this.zoneName = (zoneName != null) ? zoneName : "unknown";
+ this.zoneUuid = (zoneUuid != null) ? zoneUuid : "unknown";
+ this.podName = (podName != null) ? podName : "unknown";
+ this.podUuid = (podUuid != null) ? podUuid : "unknown";
+ this.clusterName = (clusterName != null) ? clusterName : "unknown";
+ this.clusterUuid = (clusterUuid != null) ? clusterUuid : "unknown";
+
+ this.resourceTags = (resourceTags != null) ? resourceTags : new HashMap<>();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getInternalName() {
+ return internalName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getInstanceUuid() {
+ return instanceUuid;
+ }
+
+ public Integer getCpuCores() {
+ return cpuCores;
+ }
+
+ public Integer getMemory() {
+ return memory;
+ }
+
+ public Long getCreated() { return created; }
+
+ public Long getStarted() {
+ return started;
+ }
+
+ public String getOwnerDomainUuid() {
+ return ownerDomainUuid;
+ }
+
+ public String getOwnerDomainName() {
+ return ownerDomainName;
+ }
+
+ public String getOwnerAccountUuid() {
+ return ownerAccountUuid;
+ }
+
+ public String getOwnerAccountName() {
+ return ownerAccountName;
+ }
+
+ public String getOwnerProjectUuid() {
+ return ownerProjectUuid;
+ }
+
+ public String getOwnerProjectName() {
+ return ownerProjectName;
+ }
+
+ public String getserviceOfferingName() {
+ return serviceOfferingName;
+ }
+
+ public List getserviceOfferingHostTags() {
+ return serviceOfferingHostTags;
+ }
+
+ public String getZoneName() {
+ return zoneName;
+ }
+
+ public String getZoneUuid() {
+ return zoneUuid;
+ }
+
+ public String getPodName() {
+ return podName;
+ }
+
+ public String getPodUuid() {
+ return podUuid;
+ }
+
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public String getClusterUuid() {
+ return clusterUuid;
+ }
+
+ public Map getResourceTags() { return resourceTags; }
+}
diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
index 6f24b1cd6ca8..9af6c731fd24 100644
--- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
@@ -19,20 +19,22 @@
import java.util.List;
import java.util.Map;
import java.util.HashMap;
+import java.util.stream.Collectors;
import com.cloud.agent.api.LogLevel;
import com.cloud.network.element.NetworkElement;
import com.cloud.template.VirtualMachineTemplate.BootloaderType;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.Type;
+import com.cloud.vm.VmDetailConstants;
public class VirtualMachineTO {
private long id;
private String name;
private BootloaderType bootloader;
private VirtualMachine.State state;
- Type type;
- int cpus;
+ private Type type;
+ private int cpus;
/**
'speed' is still here since 4.0.X/4.1.X management servers do not support
@@ -43,49 +45,52 @@ public class VirtualMachineTO {
So this is here for backwards compatibility with 4.0.X/4.1.X management servers
and newer agents.
*/
- Integer speed;
- Integer minSpeed;
- Integer maxSpeed;
-
- long minRam;
- long maxRam;
- String hostName;
- String arch;
- String os;
- String platformEmulator;
- String bootArgs;
- String[] bootupScripts;
- boolean enableHA;
- boolean limitCpuUse;
- boolean enableDynamicallyScaleVm;
+ private Integer speed;
+ private Integer minSpeed;
+ private Integer maxSpeed;
+
+ private long minRam;
+ private long maxRam;
+ private long requestedRam;
+ private String hostName;
+ private String arch;
+ private String os;
+ private String platformEmulator;
+ private String bootArgs;
+ private String[] bootupScripts;
+ private boolean enableHA;
+ private boolean limitCpuUse;
+ private boolean enableDynamicallyScaleVm;
@LogLevel(LogLevel.Log4jLevel.Off)
- String vncPassword;
- String vncAddr;
- Map params;
- String uuid;
- String bootType;
- String bootMode;
- boolean enterHardwareSetup;
-
- DiskTO[] disks;
- NicTO[] nics;
- GPUDeviceTO gpuDevice;
- Integer vcpuMaxLimit;
- List vmData = null;
-
- String configDriveLabel = null;
- String configDriveIsoRootFolder = null;
- String configDriveIsoFile = null;
- NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY;
-
- Double cpuQuotaPercentage = null;
-
- Map guestOsDetails = new HashMap();
- Map extraConfig = new HashMap<>();
- Map networkIdToNetworkNameMap = new HashMap<>();
- DeployAsIsInfoTO deployAsIsInfo;
- String metadataManufacturer;
- String metadataProductName;
+ private String vncPassword;
+ private String vncAddr;
+ private Map details;
+ private Map params;
+ private String uuid;
+ private String bootType;
+ private String bootMode;
+ private boolean enterHardwareSetup;
+
+ private DiskTO[] disks;
+ private NicTO[] nics;
+ private GPUDeviceTO gpuDevice;
+ private Integer vcpuMaxLimit;
+ private List vmData = null;
+
+ private String configDriveLabel = null;
+ private String configDriveIsoRootFolder = null;
+ private String configDriveIsoFile = null;
+ private NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY;
+
+ private Double cpuQuotaPercentage = null;
+
+ private Map guestOsDetails = new HashMap();
+ private Map extraConfig = new HashMap<>();
+ private Map networkIdToNetworkNameMap = new HashMap<>();
+ private DeployAsIsInfoTO deployAsIsInfo;
+ private String metadataManufacturer;
+ private String metadataProductName;
+ private VirtualMachineMetadataTO metadata;
public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader,
String os, boolean enableHA, boolean limitCpuUse, String vncPassword) {
@@ -191,7 +196,11 @@ public Integer getMaxSpeed() {
return maxSpeed;
}
- public boolean getLimitCpuUse() {
+ public boolean isEnableHA() {
+ return enableHA;
+ }
+
+ public boolean isLimitCpuUse() {
return limitCpuUse;
}
@@ -199,15 +208,20 @@ public long getMinRam() {
return minRam;
}
- public void setRam(long minRam, long maxRam) {
+ public void setRam(long minRam, long maxRam, long requestedRam) {
this.minRam = minRam;
this.maxRam = maxRam;
+ this.requestedRam = requestedRam;
}
public long getMaxRam() {
return maxRam;
}
+ public long getRequestedRam() {
+ return requestedRam;
+ }
+
public String getHostName() {
return hostName;
}
@@ -256,6 +270,10 @@ public void setBootupScripts(String[] bootupScripts) {
this.bootupScripts = bootupScripts;
}
+ public void setEnableHA(boolean enableHA) {
+ this.enableHA = enableHA;
+ }
+
public DiskTO[] getDisks() {
return disks;
}
@@ -289,11 +307,11 @@ public void setVncAddr(String vncAddr) {
}
public Map getDetails() {
- return params;
+ return details;
}
public void setDetails(Map params) {
- this.params = params;
+ this.details = params;
}
public String getUuid() {
@@ -431,6 +449,42 @@ public void setDeployAsIsInfo(DeployAsIsInfoTO deployAsIsInfo) {
this.deployAsIsInfo = deployAsIsInfo;
}
+ public void setSpeed(Integer speed) {
+ this.speed = speed;
+ }
+
+ public void setMinSpeed(Integer minSpeed) {
+ this.minSpeed = minSpeed;
+ }
+
+ public void setMaxSpeed(Integer maxSpeed) {
+ this.maxSpeed = maxSpeed;
+ }
+
+ public void setMinRam(long minRam) {
+ this.minRam = minRam;
+ }
+
+ public void setMaxRam(long maxRam) {
+ this.maxRam = maxRam;
+ }
+
+ public void setLimitCpuUse(boolean limitCpuUse) {
+ this.limitCpuUse = limitCpuUse;
+ }
+
+ public Map getParams() {
+ return params;
+ }
+
+ public void setParams(Map params) {
+ this.params = params;
+ }
+
+ public void setExtraConfig(Map extraConfig) {
+ this.extraConfig = extraConfig;
+ }
+
public String getMetadataManufacturer() {
return metadataManufacturer;
}
@@ -447,8 +501,28 @@ public void setMetadataProductName(String metadataProductName) {
this.metadataProductName = metadataProductName;
}
+ public VirtualMachineMetadataTO getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(VirtualMachineMetadataTO metadata) {
+ this.metadata = metadata;
+ }
+
@Override
public String toString() {
return String.format("VM {id: \"%s\", name: \"%s\", uuid: \"%s\", type: \"%s\"}", id, name, uuid, type);
}
+
+ public Map getExternalDetails() {
+ if (details == null) {
+ return new HashMap<>();
+ }
+ return details.entrySet().stream()
+ .filter(entry -> entry.getKey().startsWith(VmDetailConstants.EXTERNAL_DETAIL_PREFIX))
+ .collect(Collectors.toMap(
+ entry -> entry.getKey().substring(VmDetailConstants.EXTERNAL_DETAIL_PREFIX.length()),
+ Map.Entry::getValue
+ ));
+ }
}
diff --git a/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java b/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java
index 604720aaa290..5d028d31d5b6 100644
--- a/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java
+++ b/api/src/main/java/com/cloud/agent/manager/allocator/HostAllocator.java
@@ -22,19 +22,11 @@
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
-import com.cloud.offering.ServiceOffering;
import com.cloud.utils.component.Adapter;
-import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
public interface HostAllocator extends Adapter {
- /**
- * @param UserVm vm
- * @param ServiceOffering offering
- **/
- boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering);
-
/**
* Determines which physical hosts are suitable to
* allocate the guest virtual machines on
@@ -49,31 +41,6 @@ public interface HostAllocator extends Adapter {
public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo);
- /**
- * Determines which physical hosts are suitable to allocate the guest
- * virtual machines on
- *
- * Allocators must set any other hosts not considered for allocation in the
- * ExcludeList avoid. Thus the avoid set and the list of hosts suitable,
- * together must cover the entire host set in the cluster.
- *
- * @param VirtualMachineProfile
- * vmProfile
- * @param DeploymentPlan
- * plan
- * @param GuestType
- * type
- * @param ExcludeList
- * avoid
- * @param int returnUpTo (use -1 to return all possible hosts)
- * @param boolean considerReservedCapacity (default should be true, set to
- * false if host capacity calculation should not look at reserved
- * capacity)
- * @return List List of hosts that are suitable for VM allocation
- **/
-
- public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity);
-
/**
* Determines which physical hosts are suitable to allocate the guest
* virtual machines on
diff --git a/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java b/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java
index d34c09c94fde..d8aa13710e23 100644
--- a/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java
+++ b/api/src/main/java/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java
@@ -35,10 +35,10 @@ public class ListRecurringSnapshotScheduleCmd extends BaseListCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
- @Parameter(name = ApiConstants.SNAPSHOT_POLICY_ID, type = CommandType.LONG, description = "lists recurring snapshots by snapshot policy ID")
+ @Parameter(name = ApiConstants.SNAPSHOT_POLICY_ID, type = CommandType.LONG, description = "Lists recurring Snapshots by Snapshot policy ID")
private Long snapshotPolicyId;
- @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.LONG, required = true, description = "list recurring snapshots by volume ID")
+ @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.LONG, required = true, description = "List recurring Snapshots by volume ID")
private Long volumeId;
/////////////////////////////////////////////////////
diff --git a/api/src/main/java/com/cloud/capacity/Capacity.java b/api/src/main/java/com/cloud/capacity/Capacity.java
index a4e2c2a7f05d..4e584b18feee 100644
--- a/api/src/main/java/com/cloud/capacity/Capacity.java
+++ b/api/src/main/java/com/cloud/capacity/Capacity.java
@@ -34,13 +34,17 @@ public interface Capacity extends InternalIdentity, Identity {
public static final short CAPACITY_TYPE_LOCAL_STORAGE = 9;
public static final short CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET = 10;
public static final short CAPACITY_TYPE_GPU = 19;
+ public static final short CAPACITY_TYPE_OBJECT_STORAGE = 20;
+ public static final short CAPACITY_TYPE_BACKUP_STORAGE = 21;
public static final short CAPACITY_TYPE_CPU_CORE = 90;
public static final List STORAGE_CAPACITY_TYPES = List.of(CAPACITY_TYPE_STORAGE,
CAPACITY_TYPE_STORAGE_ALLOCATED,
CAPACITY_TYPE_SECONDARY_STORAGE,
- CAPACITY_TYPE_LOCAL_STORAGE);
+ CAPACITY_TYPE_LOCAL_STORAGE,
+ CAPACITY_TYPE_BACKUP_STORAGE,
+ CAPACITY_TYPE_OBJECT_STORAGE);
public Long getHostOrPoolId();
diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java
index 97d4b42974b3..729f72b23ca2 100644
--- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java
+++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java
@@ -17,18 +17,25 @@
package com.cloud.configuration;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import com.cloud.network.Network;
+import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
+import org.apache.cloudstack.api.command.admin.network.CloneNetworkOfferingCmd;
import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd;
import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
-import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
import org.apache.cloudstack.api.command.admin.network.DeleteGuestNetworkIpv6PrefixCmd;
import org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd;
import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd;
import org.apache.cloudstack.api.command.admin.network.ListGuestNetworkIpv6PrefixesCmd;
+import org.apache.cloudstack.api.command.admin.network.NetworkOfferingBaseCmd;
import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd;
import org.apache.cloudstack.api.command.admin.network.UpdatePodManagementNetworkIpRangeCmd;
+import org.apache.cloudstack.api.command.admin.offering.CloneDiskOfferingCmd;
+import org.apache.cloudstack.api.command.admin.offering.CloneServiceOfferingCmd;
import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd;
import org.apache.cloudstack.api.command.admin.offering.CreateServiceOfferingCmd;
import org.apache.cloudstack.api.command.admin.offering.DeleteDiskOfferingCmd;
@@ -101,39 +108,52 @@ public interface ConfigurationService {
*/
ServiceOffering createServiceOffering(CreateServiceOfferingCmd cmd);
+ /**
+ * Clones a service offering with optional parameter overrides
+ *
+ * @param cmd
+ * the command object that specifies the source offering ID and optional parameter overrides
+ * @return the newly created service offering cloned from source, null otherwise
+ */
+ ServiceOffering cloneServiceOffering(CloneServiceOfferingCmd cmd);
+
+ /**
+ * Clones a disk offering with optional parameter overrides
+ *
+ * @param cmd
+ * the command object that specifies the source offering ID and optional parameter overrides
+ * @return the newly created disk offering cloned from source, null otherwise
+ */
+ DiskOffering cloneDiskOffering(CloneDiskOfferingCmd cmd);
+
+ /**
+ * Clones a network offering with optional parameter overrides
+ *
+ * @param cmd
+ * the command object that specifies the source offering ID and optional parameter overrides
+ * @return the newly created network offering cloned from source, null otherwise
+ */
+ NetworkOffering cloneNetworkOffering(CloneNetworkOfferingCmd cmd);
+
/**
* Updates a service offering
*
- * @param serviceOfferingId
- * @param userId
- * @param name
- * @param displayText
- * @param offerHA
- * @param useVirtualNetwork
- * @param tags
* @return updated service offering
*/
ServiceOffering updateServiceOffering(UpdateServiceOfferingCmd cmd);
/**
* Deletes a service offering
- *
- * @param userId
- * @param serviceOfferingId
*/
boolean deleteServiceOffering(DeleteServiceOfferingCmd cmd);
/**
* Retrieve ID of domains for a service offering
- *
- * @param serviceOfferingId
*/
List getServiceOfferingDomains(Long serviceOfferingId);
/**
* Retrieve ID of domains for a service offering
- *
- * @param serviceOfferingId
*/
List getServiceOfferingZones(Long serviceOfferingId);
@@ -143,7 +163,6 @@ public interface ConfigurationService {
* @param cmd
* - the command specifying diskOfferingId, name, description, tags
* @return updated disk offering
- * @throws
*/
DiskOffering updateDiskOffering(UpdateDiskOfferingCmd cmd);
@@ -153,34 +172,22 @@ public interface ConfigurationService {
* @param cmd
* - the command specifying disk offering id
* @return true or false
- * @throws
*/
boolean deleteDiskOffering(DeleteDiskOfferingCmd cmd);
/**
* Creates a new disk offering
- *
- * @param domainId
- * @param name
- * @param description
- * @param numGibibytes
- * @param mirrored
- * @param size
* @return ID
*/
DiskOffering createDiskOffering(CreateDiskOfferingCmd cmd);
/**
* Retrieve ID of domains for a disk offering
- *
- * @param diskOfferingId
*/
List getDiskOfferingDomains(Long diskOfferingId);
/**
* Retrieve ID of domains for a disk offering
- *
- * @param diskOfferingId
*/
List getDiskOfferingZones(Long diskOfferingId);
@@ -201,11 +208,10 @@ public interface ConfigurationService {
* TODO
* @param allocationState
* TODO
+ * @param storageAccessGroups
* @return the new pod if successful, null otherwise
- * @throws
- * @throws
*/
- Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState);
+ Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState, List storageAccessGroups);
/**
* Creates a mutual exclusive IP range in the pod with same gateway, netmask.
@@ -223,8 +229,7 @@ public interface ConfigurationService {
/**
* Updates a mutually exclusive IP range in the pod.
* @param cmd - The command specifying pod ID, current Start IP, current End IP, new Start IP, new End IP.
- * @throws com.cloud.exception.ConcurrentOperationException
- * @return Success
+ * @throws com.cloud.exception.ConcurrentOperationException when this pod is already being accessed
*/
void updatePodIpRange(UpdatePodManagementNetworkIpRangeCmd cmd) throws ConcurrentOperationException;
@@ -245,9 +250,6 @@ public interface ConfigurationService {
/**
* Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system.
- *
- * @param UpdatePodCmd
- * api command
*/
Pod editPod(UpdatePodCmd cmd);
@@ -257,17 +259,12 @@ public interface ConfigurationService {
* @param cmd
* - the command containing podId
* @return true or false
- * @throws ,
*/
boolean deletePod(DeletePodCmd cmd);
/**
* Creates a new zone
- *
- * @param cmd
* @return the zone if successful, null otherwise
- * @throws
- * @throws
*/
DataCenter createZone(CreateZoneCmd cmd);
@@ -290,22 +287,7 @@ public interface ConfigurationService {
* Adds a VLAN to the database, along with an IP address range. Can add three types of VLANs: (1) zone-wide VLANs on
* the
* virtual public network (2) pod-wide direct attached VLANs (3) account-specific direct attached VLANs
- *
- * @param userId
- * @param vlanType
- * - either "DomR" (VLAN for a virtual public network) or "DirectAttached" (VLAN for IPs that will be
- * directly
- * attached to UserVMs)
- * @param zoneId
- * @param accountId
- * @param podId
- * @param add
- * @param vlanId
- * @param gateway
- * @param startIP
- * @param endIP
* @throws ResourceAllocationException TODO
- * @throws
* @return The new Vlan object
*/
Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException,
@@ -320,9 +302,6 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera
/**
* Marks the account with the default zone-id.
*
- * @param accountName
- * @param domainId
- * @param defaultZoneId
* @return The new account object
*/
Account markDefaultZone(String accountName, long domainId, long defaultZoneId);
@@ -333,7 +312,7 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera
boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd);
- NetworkOffering createNetworkOffering(CreateNetworkOfferingCmd cmd);
+ NetworkOffering createNetworkOffering(NetworkOfferingBaseCmd cmd);
NetworkOffering updateNetworkOffering(UpdateNetworkOfferingCmd cmd);
@@ -344,14 +323,12 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera
/**
* Retrieve ID of domains for a network offering
*
- * @param networkOfferingId
*/
List getNetworkOfferingDomains(Long networkOfferingId);
/**
* Retrieve ID of domains for a network offering
*
- * @param networkOfferingId
*/
List getNetworkOfferingZones(Long networkOfferingId);
@@ -372,4 +349,16 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera
List extends PortableIp> listPortableIps(long id);
Boolean isAccountAllowedToCreateOfferingsWithTags(IsAccountAllowedToCreateOfferingsWithTagsCmd cmd);
+
+ public static final Map ProviderDetailKeyMap = Map.of(
+ Network.Provider.Nsx.getName(), ApiConstants.NSX_DETAIL_KEY,
+ Network.Provider.Netris.getName(), ApiConstants.NETRIS_DETAIL_KEY
+ );
+
+ public static boolean IsIpRangeForProvider(Network.Provider provider) {
+ if (Objects.isNull(provider)) {
+ return false;
+ }
+ return ProviderDetailKeyMap.containsKey(provider.getName());
+ }
}
diff --git a/api/src/main/java/com/cloud/configuration/Resource.java b/api/src/main/java/com/cloud/configuration/Resource.java
index c7bf44de76c6..97be7f9d64c5 100644
--- a/api/src/main/java/com/cloud/configuration/Resource.java
+++ b/api/src/main/java/com/cloud/configuration/Resource.java
@@ -37,7 +37,8 @@ enum ResourceType { // All storage type resources are allocated_storage and not
backup("backup", 12),
backup_storage("backup_storage", 13),
bucket("bucket", 14),
- object_storage("object_storage", 15);
+ object_storage("object_storage", 15),
+ gpu("gpu", 16);
private String name;
private int ordinal;
diff --git a/api/src/main/java/com/cloud/cpu/CPU.java b/api/src/main/java/com/cloud/cpu/CPU.java
index 4e1b9f5a5011..11b38b73da53 100644
--- a/api/src/main/java/com/cloud/cpu/CPU.java
+++ b/api/src/main/java/com/cloud/cpu/CPU.java
@@ -16,52 +16,56 @@
// under the License.
package com.cloud.cpu;
-import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.commons.lang3.StringUtils;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
public class CPU {
+ public enum CPUArch {
+ x86("i686", 32),
+ amd64("x86_64", 64),
+ arm64("aarch64", 64),
+ s390x("s390x", 64);
- public static final String archX86Identifier = "i686";
- public static final String archX86_64Identifier = "x86_64";
- public static final String archARM64Identifier = "aarch64";
-
- public static class CPUArch {
- private static final Map cpuArchMap = new LinkedHashMap<>();
-
- public static final CPUArch archX86 = new CPUArch(archX86Identifier, 32);
- public static final CPUArch amd64 = new CPUArch(archX86_64Identifier, 64);
- public static final CPUArch arm64 = new CPUArch(archARM64Identifier, 64);
+ private final String type;
+ private final int bits;
- private String type;
- private int bits;
-
- public CPUArch(String type, int bits) {
+ CPUArch(String type, int bits) {
this.type = type;
this.bits = bits;
- cpuArchMap.put(type, this);
+ }
+
+ public static CPUArch getDefault() {
+ return amd64;
}
public String getType() {
- return this.type;
+ return type;
}
public int getBits() {
- return this.bits;
+ return bits;
}
public static CPUArch fromType(String type) {
if (StringUtils.isBlank(type)) {
- return amd64;
+ return getDefault();
+ }
+ for (CPUArch arch : values()) {
+ if (arch.type.equals(type)) {
+ return arch;
+ }
+ }
+ throw new IllegalArgumentException("Unsupported arch type: " + type);
+ }
+
+ public static String getTypesAsCSV() {
+ StringBuilder sb = new StringBuilder();
+ for (CPUArch arch : values()) {
+ sb.append(arch.getType()).append(",");
}
- switch (type) {
- case archX86Identifier: return archX86;
- case archX86_64Identifier: return amd64;
- case archARM64Identifier: return arm64;
- default: throw new CloudRuntimeException(String.format("Unsupported arch type: %s", type));
+ if (sb.length() > 0) {
+ sb.setLength(sb.length() - 1);
}
+ return sb.toString();
}
}
}
diff --git a/api/src/main/java/com/cloud/dc/Pod.java b/api/src/main/java/com/cloud/dc/Pod.java
index 1cbab36f3bd4..17c5b615d4b6 100644
--- a/api/src/main/java/com/cloud/dc/Pod.java
+++ b/api/src/main/java/com/cloud/dc/Pod.java
@@ -43,4 +43,6 @@ public interface Pod extends InfrastructureEntity, Grouping, Identity, InternalI
AllocationState getAllocationState();
boolean getExternalDhcp();
+
+ String getStorageAccessGroups();
}
diff --git a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java
index 2697311d2b94..9471c3d5c84c 100644
--- a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java
+++ b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java
@@ -62,11 +62,11 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner {
"vm.allocation.algorithm",
"Advanced",
"random",
- "Order in which hosts within a cluster will be considered for VM/volume allocation. The value can be 'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', or 'firstfitleastconsumed'.",
+ "Order in which hosts within a cluster will be considered for VM allocation. The value can be 'random', 'firstfit', 'userdispersing', or 'firstfitleastconsumed'.",
true,
ConfigKey.Scope.Global, null, null, null, null, null,
ConfigKey.Kind.Select,
- "random,firstfit,userdispersing,userconcentratedpod_random,userconcentratedpod_firstfit,firstfitleastconsumed");
+ "random,firstfit,userdispersing,firstfitleastconsumed");
/**
* This is called to determine list of possible clusters where a virtual
diff --git a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java
index 354f9cfaac53..22d796d4a775 100644
--- a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java
+++ b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java
@@ -70,7 +70,7 @@ public interface DeploymentPlanner extends Adapter {
boolean canHandle(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid);
public enum AllocationAlgorithm {
- random, firstfit, userdispersing, userconcentratedpod_random, userconcentratedpod_firstfit;
+ random, firstfit, userdispersing, firstfitleastconsumed;
}
public enum PlannerResourceUsage {
diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index 862a6e21fa82..c97e916fe430 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -27,17 +27,28 @@
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.BackupRepositoryService;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet;
+import org.apache.cloudstack.dns.DnsRecord;
+import org.apache.cloudstack.dns.DnsServer;
+import org.apache.cloudstack.dns.DnsZone;
+import org.apache.cloudstack.extension.Extension;
+import org.apache.cloudstack.extension.ExtensionCustomAction;
+import org.apache.cloudstack.gpu.GpuCard;
+import org.apache.cloudstack.gpu.GpuDevice;
+import org.apache.cloudstack.gpu.VgpuProfile;
import org.apache.cloudstack.ha.HAConfig;
+import org.apache.cloudstack.kms.HSMProfile;
+import org.apache.cloudstack.kms.KMSKey;
import org.apache.cloudstack.network.BgpPeer;
import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap;
import org.apache.cloudstack.quota.QuotaTariff;
-import org.apache.cloudstack.storage.sharedfs.SharedFS;
import org.apache.cloudstack.storage.object.Bucket;
import org.apache.cloudstack.storage.object.ObjectStore;
+import org.apache.cloudstack.storage.sharedfs.SharedFS;
import org.apache.cloudstack.usage.Usage;
-import org.apache.cloudstack.vm.schedule.VMSchedule;
+import org.apache.cloudstack.schedule.ResourceSchedule;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterGuestIpv6Prefix;
@@ -119,17 +130,18 @@ public class EventTypes {
public static final String EVENT_VM_UNMANAGE = "VM.UNMANAGE";
public static final String EVENT_VM_RECOVER = "VM.RECOVER";
- // VM Schedule
- public static final String EVENT_VM_SCHEDULE_CREATE = "VM.SCHEDULE.CREATE";
- public static final String EVENT_VM_SCHEDULE_UPDATE = "VM.SCHEDULE.UPDATE";
- public static final String EVENT_VM_SCHEDULE_DELETE = "VM.SCHEDULE.DELETE";
-
+ // VM Schedule action-execution events (fired when a scheduled action runs).
public static final String EVENT_VM_SCHEDULE_START = "VM.SCHEDULE.START";
public static final String EVENT_VM_SCHEDULE_STOP = "VM.SCHEDULE.STOP";
public static final String EVENT_VM_SCHEDULE_REBOOT = "VM.SCHEDULE.REBOOT";
public static final String EVENT_VM_SCHEDULE_FORCE_STOP = "VM.SCHEDULE.FORCE.STOP";
public static final String EVENT_VM_SCHEDULE_FORCE_REBOOT = "VM.SCHEDULE.FORCE.REBOOT";
+ // Generic Resource Schedule CRUD events (apply to all resource types).
+ public static final String EVENT_SCHEDULE_CREATE = "SCHEDULE.CREATE";
+ public static final String EVENT_SCHEDULE_UPDATE = "SCHEDULE.UPDATE";
+ public static final String EVENT_SCHEDULE_DELETE = "SCHEDULE.DELETE";
+
// Domain Router
public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
public static final String EVENT_ROUTER_DESTROY = "ROUTER.DESTROY";
@@ -265,6 +277,20 @@ public class EventTypes {
public static final String EVENT_CA_CERTIFICATE_REVOKE = "CA.CERTIFICATE.REVOKE";
public static final String EVENT_CA_CERTIFICATE_PROVISION = "CA.CERTIFICATE.PROVISION";
+ // KMS (Key Management Service) events
+ public static final String EVENT_KMS_KEY_WRAP = "KMS.KEY.WRAP";
+ public static final String EVENT_KMS_KEY_UNWRAP = "KMS.KEY.UNWRAP";
+ public static final String EVENT_KMS_KEY_CREATE = "KMS.KEY.CREATE";
+ public static final String EVENT_KMS_KEY_UPDATE = "KMS.KEY.UPDATE";
+ public static final String EVENT_KMS_KEY_ROTATE = "KMS.KEY.ROTATE";
+ public static final String EVENT_KMS_KEY_DELETE = "KMS.KEY.DELETE";
+ public static final String EVENT_VOLUME_MIGRATE_TO_KMS = "VOLUME.MIGRATE.TO.KMS";
+
+ // HSM Profile events
+ public static final String EVENT_HSM_PROFILE_CREATE = "HSM.PROFILE.CREATE";
+ public static final String EVENT_HSM_PROFILE_UPDATE = "HSM.PROFILE.UPDATE";
+ public static final String EVENT_HSM_PROFILE_DELETE = "HSM.PROFILE.DELETE";
+
// Account events
public static final String EVENT_ACCOUNT_ENABLE = "ACCOUNT.ENABLE";
public static final String EVENT_ACCOUNT_DISABLE = "ACCOUNT.DISABLE";
@@ -289,9 +315,12 @@ public class EventTypes {
//registering userdata events
public static final String EVENT_REGISTER_USER_DATA = "REGISTER.USER.DATA";
+ public static final String EVENT_REGISTER_CNI_CONFIG = "REGISTER.CNI.CONFIG";
+ public static final String EVENT_DELETE_CNI_CONFIG = "DELETE.CNI.CONFIG";
- //register for user API and secret keys
+ //user API and secret keys
public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY";
+ public static final String EVENT_DELETE_SECRET_API_KEY = "DELETE.USER.KEY";
public static final String API_KEY_ACCESS_UPDATE = "API.KEY.ACCESS.UPDATE";
// Template Events
@@ -366,16 +395,34 @@ public class EventTypes {
// Service Offerings
public static final String EVENT_SERVICE_OFFERING_CREATE = "SERVICE.OFFERING.CREATE";
+ public static final String EVENT_SERVICE_OFFERING_CLONE = "SERVICE.OFFERING.CLONE";
public static final String EVENT_SERVICE_OFFERING_EDIT = "SERVICE.OFFERING.EDIT";
public static final String EVENT_SERVICE_OFFERING_DELETE = "SERVICE.OFFERING.DELETE";
// Disk Offerings
public static final String EVENT_DISK_OFFERING_CREATE = "DISK.OFFERING.CREATE";
+ public static final String EVENT_DISK_OFFERING_CLONE = "DISK.OFFERING.CLONE";
public static final String EVENT_DISK_OFFERING_EDIT = "DISK.OFFERING.EDIT";
public static final String EVENT_DISK_OFFERING_DELETE = "DISK.OFFERING.DELETE";
+ // GPU Cards
+ public static final String EVENT_GPU_CARD_CREATE = "GPU.CARD.CREATE";
+ public static final String EVENT_GPU_CARD_EDIT = "GPU.CARD.EDIT";
+ public static final String EVENT_GPU_CARD_DELETE = "GPU.CARD.DELETE";
+
+ // vGPU Profile
+ public static final String EVENT_VGPU_PROFILE_CREATE = "VGPU.PROFILE.CREATE";
+ public static final String EVENT_VGPU_PROFILE_EDIT = "VGPU.PROFILE.EDIT";
+ public static final String EVENT_VGPU_PROFILE_DELETE = "VGPU.PROFILE.DELETE";
+
+ // GPU Devices
+ public static final String EVENT_GPU_DEVICE_CREATE = "GPU.DEVICE.CREATE";
+ public static final String EVENT_GPU_DEVICE_EDIT = "GPU.DEVICE.EDIT";
+ public static final String EVENT_GPU_DEVICE_DELETE = "GPU.DEVICE.DELETE";
+
// Network offerings
public static final String EVENT_NETWORK_OFFERING_CREATE = "NETWORK.OFFERING.CREATE";
+ public static final String EVENT_NETWORK_OFFERING_CLONE = "NETWORK.OFFERING.CLONE";
public static final String EVENT_NETWORK_OFFERING_ASSIGN = "NETWORK.OFFERING.ASSIGN";
public static final String EVENT_NETWORK_OFFERING_EDIT = "NETWORK.OFFERING.EDIT";
public static final String EVENT_NETWORK_OFFERING_REMOVE = "NETWORK.OFFERING.REMOVE";
@@ -465,6 +512,7 @@ public class EventTypes {
public static final String EVENT_ENABLE_PRIMARY_STORAGE = "ENABLE.PS";
public static final String EVENT_DISABLE_PRIMARY_STORAGE = "DISABLE.PS";
public static final String EVENT_SYNC_STORAGE_POOL = "SYNC.STORAGE.POOL";
+ public static final String EVENT_CONFIGURE_STORAGE_ACCESS = "CONFIGURE.STORAGE.ACCESS";
public static final String EVENT_CHANGE_STORAGE_POOL_SCOPE = "CHANGE.STORAGE.POOL.SCOPE";
// VPN
@@ -479,6 +527,7 @@ public class EventTypes {
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE = "VPN.S2S.CUSTOMER.GATEWAY.CREATE";
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE = "VPN.S2S.CUSTOMER.GATEWAY.DELETE";
public static final String EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE = "VPN.S2S.CUSTOMER.GATEWAY.UPDATE";
+ public static final String EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS = "VPN.S2S.GATEWAY.OBSOLETE.PARAMS";
public static final String EVENT_S2S_VPN_CONNECTION_CREATE = "VPN.S2S.CONNECTION.CREATE";
public static final String EVENT_S2S_VPN_CONNECTION_DELETE = "VPN.S2S.CONNECTION.DELETE";
public static final String EVENT_S2S_VPN_CONNECTION_RESET = "VPN.S2S.CONNECTION.RESET";
@@ -496,6 +545,8 @@ public class EventTypes {
public static final String EVENT_ZONE_VLAN_ASSIGN = "ZONE.VLAN.ASSIGN";
public static final String EVENT_ZONE_VLAN_RELEASE = "ZONE.VLAN.RELEASE";
+ public static final String EVENT_ZONE_VXLAN_ASSIGN = "ZONE.VXLAN.ASSIGN";
+ public static final String EVENT_ZONE_VXLAN_RELEASE = "ZONE.VXLAN.RELEASE";
// Projects
public static final String EVENT_PROJECT_CREATE = "PROJECT.CREATE";
@@ -556,6 +607,7 @@ public class EventTypes {
// Network ACL
public static final String EVENT_NETWORK_ACL_CREATE = "NETWORK.ACL.CREATE";
+ public static final String EVENT_NETWORK_ACL_IMPORT = "NETWORK.ACL.IMPORT";
public static final String EVENT_NETWORK_ACL_DELETE = "NETWORK.ACL.DELETE";
public static final String EVENT_NETWORK_ACL_REPLACE = "NETWORK.ACL.REPLACE";
public static final String EVENT_NETWORK_ACL_UPDATE = "NETWORK.ACL.UPDATE";
@@ -570,6 +622,7 @@ public class EventTypes {
// VPC offerings
public static final String EVENT_VPC_OFFERING_CREATE = "VPC.OFFERING.CREATE";
+ public static final String EVENT_VPC_OFFERING_CLONE = "VPC.OFFERING.CLONE";
public static final String EVENT_VPC_OFFERING_UPDATE = "VPC.OFFERING.UPDATE";
public static final String EVENT_VPC_OFFERING_DELETE = "VPC.OFFERING.DELETE";
@@ -602,16 +655,19 @@ public class EventTypes {
// Backup and Recovery events
public static final String EVENT_VM_BACKUP_IMPORT_OFFERING = "BACKUP.IMPORT.OFFERING";
+ public static final String EVENT_VM_BACKUP_OFFERING_CLONE = "BACKUP.OFFERING.CLONE";
public static final String EVENT_VM_BACKUP_OFFERING_ASSIGN = "BACKUP.OFFERING.ASSIGN";
public static final String EVENT_VM_BACKUP_OFFERING_REMOVE = "BACKUP.OFFERING.REMOVE";
public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE";
public static final String EVENT_VM_BACKUP_RESTORE = "BACKUP.RESTORE";
public static final String EVENT_VM_BACKUP_DELETE = "BACKUP.DELETE";
+ public static final String EVENT_VM_BACKUP_OFFERING_REMOVED_AND_BACKUPS_DELETED = "BACKUP.OFFERING.BACKUPS.DEL";
public static final String EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM = "BACKUP.RESTORE.VOLUME.TO.VM";
public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE";
public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE";
public static final String EVENT_VM_BACKUP_USAGE_METRIC = "BACKUP.USAGE.METRIC";
public static final String EVENT_VM_BACKUP_EDIT = "BACKUP.OFFERING.EDIT";
+ public static final String EVENT_VM_CREATE_FROM_BACKUP = "VM.CREATE.FROM.BACKUP";
// external network device events
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";
@@ -640,6 +696,7 @@ public class EventTypes {
public static final String EVENT_AUTOSCALEVMGROUP_DISABLE = "AUTOSCALEVMGROUP.DISABLE";
public static final String EVENT_AUTOSCALEVMGROUP_SCALEDOWN = "AUTOSCALEVMGROUP.SCALEDOWN";
public static final String EVENT_AUTOSCALEVMGROUP_SCALEUP = "AUTOSCALEVMGROUP.SCALEUP";
+ public static final String EVENT_AUTOSCALEVMGROUP_SCHEDULE_UPDATE = "AUTOSCALEVMGROUP.SCHEDULE.UPDATE";
public static final String EVENT_BAREMETAL_DHCP_SERVER_ADD = "PHYSICAL.DHCP.ADD";
public static final String EVENT_BAREMETAL_DHCP_SERVER_DELETE = "PHYSICAL.DHCP.DELETE";
@@ -687,6 +744,9 @@ public class EventTypes {
public static final String EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.CONFIGURE";
//Guest OS related events
+ public static final String EVENT_GUEST_OS_CATEGORY_ADD = "GUEST.OS.CATEGORY.ADD";
+ public static final String EVENT_GUEST_OS_CATEGORY_DELETE = "GUEST.OS.CATEGORY.DELETE";
+ public static final String EVENT_GUEST_OS_CATEGORY_UPDATE = "GUEST.OS.CATEGORY.UPDATE";
public static final String EVENT_GUEST_OS_ADD = "GUEST.OS.ADD";
public static final String EVENT_GUEST_OS_REMOVE = "GUEST.OS.REMOVE";
public static final String EVENT_GUEST_OS_UPDATE = "GUEST.OS.UPDATE";
@@ -739,6 +799,13 @@ public class EventTypes {
//Purge resources
public static final String EVENT_PURGE_EXPUNGED_RESOURCES = "PURGE.EXPUNGED.RESOURCES";
+ // Management Server
+ public static final String EVENT_MS_MAINTENANCE_PREPARE = "MS.MAINTENANCE.PREPARE";
+ public static final String EVENT_MS_MAINTENANCE_CANCEL = "MS.MAINTENANCE.CANCEL";
+ public static final String EVENT_MS_SHUTDOWN_PREPARE = "MS.SHUTDOWN.PREPARE";
+ public static final String EVENT_MS_SHUTDOWN_CANCEL = "MS.SHUTDOWN.CANCEL";
+ public static final String EVENT_MS_SHUTDOWN = "MS.SHUTDOWN";
+
// OBJECT STORE
public static final String EVENT_OBJECT_STORE_CREATE = "OBJECT.STORE.CREATE";
public static final String EVENT_OBJECT_STORE_DELETE = "OBJECT.STORE.DELETE";
@@ -788,6 +855,48 @@ public class EventTypes {
// Resource Limit
public static final String EVENT_RESOURCE_LIMIT_UPDATE = "RESOURCE.LIMIT.UPDATE";
+ // Management Server
+ public static final String EVENT_MANAGEMENT_SERVER_REMOVE = "MANAGEMENT.SERVER.REMOVE";
+
+ // VM Lease
+ public static final String VM_LEASE_EXPIRED = "VM.LEASE.EXPIRED";
+ public static final String VM_LEASE_DISABLED = "VM.LEASE.DISABLED";
+ public static final String VM_LEASE_CANCELLED = "VM.LEASE.CANCELLED";
+ public static final String VM_LEASE_EXPIRING = "VM.LEASE.EXPIRING";
+
+ // GUI Theme
+ public static final String EVENT_GUI_THEME_CREATE = "GUI.THEME.CREATE";
+ public static final String EVENT_GUI_THEME_REMOVE = "GUI.THEME.REMOVE";
+ public static final String EVENT_GUI_THEME_UPDATE = "GUI.THEME.UPDATE";
+
+ // Extension
+ public static final String EVENT_EXTENSION_CREATE = "EXTENSION.CREATE";
+ public static final String EVENT_EXTENSION_UPDATE = "EXTENSION.UPDATE";
+ public static final String EVENT_EXTENSION_DELETE = "EXTENSION.DELETE";
+ public static final String EVENT_EXTENSION_RESOURCE_REGISTER = "EXTENSION.RESOURCE.REGISTER";
+ public static final String EVENT_EXTENSION_RESOURCE_UNREGISTER = "EXTENSION.RESOURCE.UNREGISTER";
+ public static final String EVENT_EXTENSION_CUSTOM_ACTION_ADD = "EXTENSION.CUSTOM.ACTION.ADD";
+ public static final String EVENT_EXTENSION_CUSTOM_ACTION_UPDATE = "EXTENSION.CUSTOM.ACTION.UPDATE";
+ public static final String EVENT_EXTENSION_CUSTOM_ACTION_DELETE = "EXTENSION.CUSTOM.ACTION.DELETE";
+
+ // Custom Action
+ public static final String EVENT_CUSTOM_ACTION = "CUSTOM.ACTION";
+
+ // Backup Repository
+ public static final String EVENT_BACKUP_REPOSITORY_ADD = "BACKUP.REPOSITORY.ADD";
+ public static final String EVENT_BACKUP_REPOSITORY_UPDATE = "BACKUP.REPOSITORY.UPDATE";
+
+ // DNS Framework Events
+ public static final String EVENT_DNS_SERVER_ADD = "DNS.SERVER.ADD";
+ public static final String EVENT_DNS_SERVER_UPDATE = "DNS.SERVER.UPDATE";
+ public static final String EVENT_DNS_SERVER_DELETE = "DNS.SERVER.DELETE";
+ public static final String EVENT_DNS_ZONE_CREATE = "DNS.ZONE.CREATE";
+ public static final String EVENT_DNS_ZONE_UPDATE = "DNS.ZONE.UPDATE";
+ public static final String EVENT_DNS_ZONE_DELETE = "DNS.ZONE.DELETE";
+ public static final String EVENT_DNS_RECORD_CREATE = "DNS.RECORD.CREATE";
+ public static final String EVENT_DNS_RECORD_DELETE = "DNS.RECORD.DELETE";
+ public static final String EVENT_DNS_NAME_COLLISION = "DNS.NAME.COLLISION";
+
static {
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
@@ -811,15 +920,18 @@ public class EventTypes {
entityEventDetails.put(EVENT_VM_IMPORT, VirtualMachine.class);
entityEventDetails.put(EVENT_VM_UNMANAGE, VirtualMachine.class);
- // VMSchedule
- entityEventDetails.put(EVENT_VM_SCHEDULE_CREATE, VMSchedule.class);
- entityEventDetails.put(EVENT_VM_SCHEDULE_DELETE, VMSchedule.class);
- entityEventDetails.put(EVENT_VM_SCHEDULE_UPDATE, VMSchedule.class);
- entityEventDetails.put(EVENT_VM_SCHEDULE_START, VMSchedule.class);
- entityEventDetails.put(EVENT_VM_SCHEDULE_STOP, VMSchedule.class);
- entityEventDetails.put(EVENT_VM_SCHEDULE_REBOOT, VMSchedule.class);
- entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_STOP, VMSchedule.class);
- entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_REBOOT, VMSchedule.class);
+ // VMSchedule action-execution events
+ entityEventDetails.put(EVENT_VM_SCHEDULE_START, ResourceSchedule.class);
+ entityEventDetails.put(EVENT_VM_SCHEDULE_STOP, ResourceSchedule.class);
+ entityEventDetails.put(EVENT_VM_SCHEDULE_REBOOT, ResourceSchedule.class);
+ entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_STOP, ResourceSchedule.class);
+ entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_REBOOT, ResourceSchedule.class);
+ entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_SCHEDULE_UPDATE, ResourceSchedule.class);
+
+ // Generic Resource Schedule
+ entityEventDetails.put(EVENT_SCHEDULE_CREATE, ResourceSchedule.class);
+ entityEventDetails.put(EVENT_SCHEDULE_UPDATE, ResourceSchedule.class);
+ entityEventDetails.put(EVENT_SCHEDULE_DELETE, ResourceSchedule.class);
entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class);
entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class);
@@ -938,6 +1050,20 @@ public class EventTypes {
entityEventDetails.put(EVENT_VOLUME_RECOVER, Volume.class);
entityEventDetails.put(EVENT_VOLUME_CHANGE_DISK_OFFERING, Volume.class);
+ // KMS Key Events
+ entityEventDetails.put(EVENT_KMS_KEY_CREATE, KMSKey.class);
+ entityEventDetails.put(EVENT_KMS_KEY_UPDATE, KMSKey.class);
+ entityEventDetails.put(EVENT_KMS_KEY_UNWRAP, KMSKey.class);
+ entityEventDetails.put(EVENT_KMS_KEY_WRAP, KMSKey.class);
+ entityEventDetails.put(EVENT_KMS_KEY_DELETE, KMSKey.class);
+ entityEventDetails.put(EVENT_KMS_KEY_ROTATE, KMSKey.class);
+ entityEventDetails.put(EVENT_VOLUME_MIGRATE_TO_KMS, KMSKey.class);
+
+ // HSM Profile Events
+ entityEventDetails.put(EVENT_HSM_PROFILE_CREATE, HSMProfile.class);
+ entityEventDetails.put(EVENT_HSM_PROFILE_UPDATE, HSMProfile.class);
+ entityEventDetails.put(EVENT_HSM_PROFILE_DELETE, HSMProfile.class);
+
// Domains
entityEventDetails.put(EVENT_DOMAIN_CREATE, Domain.class);
entityEventDetails.put(EVENT_DOMAIN_DELETE, Domain.class);
@@ -974,16 +1100,34 @@ public class EventTypes {
// Service Offerings
entityEventDetails.put(EVENT_SERVICE_OFFERING_CREATE, ServiceOffering.class);
+ entityEventDetails.put(EVENT_SERVICE_OFFERING_CLONE, ServiceOffering.class);
entityEventDetails.put(EVENT_SERVICE_OFFERING_EDIT, ServiceOffering.class);
entityEventDetails.put(EVENT_SERVICE_OFFERING_DELETE, ServiceOffering.class);
// Disk Offerings
entityEventDetails.put(EVENT_DISK_OFFERING_CREATE, DiskOffering.class);
+ entityEventDetails.put(EVENT_DISK_OFFERING_CLONE, DiskOffering.class);
entityEventDetails.put(EVENT_DISK_OFFERING_EDIT, DiskOffering.class);
entityEventDetails.put(EVENT_DISK_OFFERING_DELETE, DiskOffering.class);
+ // GPU Cards
+ entityEventDetails.put(EVENT_GPU_CARD_CREATE, GpuCard.class);
+ entityEventDetails.put(EVENT_GPU_CARD_EDIT, GpuCard.class);
+ entityEventDetails.put(EVENT_GPU_CARD_DELETE, GpuCard.class);
+
+ // vGPU Profiles
+ entityEventDetails.put(EVENT_VGPU_PROFILE_CREATE, VgpuProfile.class);
+ entityEventDetails.put(EVENT_VGPU_PROFILE_EDIT, VgpuProfile.class);
+ entityEventDetails.put(EVENT_VGPU_PROFILE_DELETE, VgpuProfile.class);
+
+ // GPU Devices
+ entityEventDetails.put(EVENT_GPU_DEVICE_CREATE, GpuDevice.class);
+ entityEventDetails.put(EVENT_GPU_DEVICE_EDIT, GpuDevice.class);
+ entityEventDetails.put(EVENT_GPU_DEVICE_DELETE, GpuDevice.class);
+
// Network offerings
entityEventDetails.put(EVENT_NETWORK_OFFERING_CREATE, NetworkOffering.class);
+ entityEventDetails.put(EVENT_NETWORK_OFFERING_CLONE, NetworkOffering.class);
entityEventDetails.put(EVENT_NETWORK_OFFERING_ASSIGN, NetworkOffering.class);
entityEventDetails.put(EVENT_NETWORK_OFFERING_EDIT, NetworkOffering.class);
entityEventDetails.put(EVENT_NETWORK_OFFERING_REMOVE, NetworkOffering.class);
@@ -1067,6 +1211,7 @@ public class EventTypes {
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_CREATE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_DELETE, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CUSTOMER_GATEWAY_UPDATE, Site2SiteCustomerGateway.class);
+ entityEventDetails.put(EVENT_S2S_VPN_GATEWAY_OBSOLETE_PARAMS, Site2SiteCustomerGateway.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_CREATE, Site2SiteVpnConnection.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_DELETE, Site2SiteVpnConnection.class);
entityEventDetails.put(EVENT_S2S_VPN_CONNECTION_RESET, Site2SiteVpnConnection.class);
@@ -1233,6 +1378,12 @@ public class EventTypes {
entityEventDetails.put(EVENT_UPDATE_IMAGE_STORE_ACCESS_STATE, ImageStore.class);
entityEventDetails.put(EVENT_LIVE_PATCH_SYSTEMVM, "SystemVMs");
+ entityEventDetails.put(EVENT_MS_MAINTENANCE_PREPARE, "ManagementServer");
+ entityEventDetails.put(EVENT_MS_MAINTENANCE_CANCEL, "ManagementServer");
+ entityEventDetails.put(EVENT_MS_SHUTDOWN_PREPARE, "ManagementServer");
+ entityEventDetails.put(EVENT_MS_SHUTDOWN_CANCEL, "ManagementServer");
+ entityEventDetails.put(EVENT_MS_SHUTDOWN, "ManagementServer");
+
//Object Store
entityEventDetails.put(EVENT_OBJECT_STORE_CREATE, ObjectStore.class);
entityEventDetails.put(EVENT_OBJECT_STORE_UPDATE, ObjectStore.class);
@@ -1276,6 +1427,45 @@ public class EventTypes {
entityEventDetails.put(EVENT_SHAREDFS_DESTROY, SharedFS.class);
entityEventDetails.put(EVENT_SHAREDFS_EXPUNGE, SharedFS.class);
entityEventDetails.put(EVENT_SHAREDFS_RECOVER, SharedFS.class);
+
+ // Management Server
+ entityEventDetails.put(EVENT_MANAGEMENT_SERVER_REMOVE, "ManagementServer");
+
+ // VM Lease
+ entityEventDetails.put(VM_LEASE_EXPIRED, VirtualMachine.class);
+ entityEventDetails.put(VM_LEASE_EXPIRING, VirtualMachine.class);
+ entityEventDetails.put(VM_LEASE_DISABLED, VirtualMachine.class);
+ entityEventDetails.put(VM_LEASE_CANCELLED, VirtualMachine.class);
+
+ // GUI theme
+ entityEventDetails.put(EVENT_GUI_THEME_CREATE, "GuiTheme");
+ entityEventDetails.put(EVENT_GUI_THEME_REMOVE, "GuiTheme");
+ entityEventDetails.put(EVENT_GUI_THEME_UPDATE, "GuiTheme");
+
+ // Extension
+ entityEventDetails.put(EVENT_EXTENSION_CREATE, Extension.class);
+ entityEventDetails.put(EVENT_EXTENSION_UPDATE, Extension.class);
+ entityEventDetails.put(EVENT_EXTENSION_DELETE, Extension.class);
+ entityEventDetails.put(EVENT_EXTENSION_RESOURCE_REGISTER, Extension.class);
+ entityEventDetails.put(EVENT_EXTENSION_RESOURCE_UNREGISTER, Extension.class);
+ entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_ADD, ExtensionCustomAction.class);
+ entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_UPDATE, ExtensionCustomAction.class);
+ entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_DELETE, ExtensionCustomAction.class);
+
+ // Backup Repository
+ entityEventDetails.put(EVENT_BACKUP_REPOSITORY_ADD, BackupRepositoryService.class);
+ entityEventDetails.put(EVENT_BACKUP_REPOSITORY_UPDATE, BackupRepositoryService.class);
+
+ // DNS Framework Events
+ entityEventDetails.put(EVENT_DNS_SERVER_ADD, DnsServer.class);
+ entityEventDetails.put(EVENT_DNS_SERVER_UPDATE, DnsServer.class);
+ entityEventDetails.put(EVENT_DNS_SERVER_DELETE, DnsServer.class);
+ entityEventDetails.put(EVENT_DNS_ZONE_CREATE, DnsZone.class);
+ entityEventDetails.put(EVENT_DNS_ZONE_UPDATE, DnsZone.class);
+ entityEventDetails.put(EVENT_DNS_ZONE_DELETE, DnsZone.class);
+ entityEventDetails.put(EVENT_DNS_RECORD_CREATE, DnsRecord.class);
+ entityEventDetails.put(EVENT_DNS_RECORD_DELETE, DnsRecord.class);
+
}
public static boolean isNetworkEvent(String eventType) {
diff --git a/api/src/main/java/com/cloud/exception/OperationTimedoutException.java b/api/src/main/java/com/cloud/exception/OperationTimedoutException.java
index fe27408eb4e3..66b607100d97 100644
--- a/api/src/main/java/com/cloud/exception/OperationTimedoutException.java
+++ b/api/src/main/java/com/cloud/exception/OperationTimedoutException.java
@@ -40,7 +40,7 @@ public class OperationTimedoutException extends CloudException {
boolean _isActive;
public OperationTimedoutException(Command[] cmds, long agentId, long seqId, int time, boolean isActive) {
- super("Commands " + seqId + " to Host " + agentId + " timed out after " + time);
+ super("Commands " + seqId + " to Host " + agentId + " timed out after " + time + " secs");
_agentId = agentId;
_seqId = seqId;
_time = time;
diff --git a/api/src/main/java/com/cloud/ha/Investigator.java b/api/src/main/java/com/cloud/ha/Investigator.java
index 88d802a1ce44..00371d395f5a 100644
--- a/api/src/main/java/com/cloud/ha/Investigator.java
+++ b/api/src/main/java/com/cloud/ha/Investigator.java
@@ -26,17 +26,19 @@ public interface Investigator extends Adapter {
* Returns if the vm is still alive.
*
* @param vm to work on.
+ * @return true if vm is alive, otherwise false
*/
- public boolean isVmAlive(VirtualMachine vm, Host host) throws UnknownVM;
+ boolean isVmAlive(VirtualMachine vm, Host host) throws UnknownVM;
- public Status isAgentAlive(Host agent);
+ /**
+ * Returns the agent status of the host.
+ *
+ * @param host
+ * @return status of the host agent
+ */
+ Status getHostAgentStatus(Host host);
class UnknownVM extends Exception {
-
- /**
- *
- */
private static final long serialVersionUID = 1L;
-
};
}
diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java
index afac6df56312..c110e4ca94e1 100644
--- a/api/src/main/java/com/cloud/host/Host.java
+++ b/api/src/main/java/com/cloud/host/Host.java
@@ -53,9 +53,21 @@ public static String[] toStrings(Host.Type... types) {
return strs;
}
}
- public static final String HOST_UEFI_ENABLE = "host.uefi.enable";
- public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption";
- public static final String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
+
+ String HOST_UEFI_ENABLE = "host.uefi.enable";
+ String HOST_VOLUME_ENCRYPTION = "host.volume.encryption";
+ String HOST_INSTANCE_CONVERSION = "host.instance.conversion";
+ String HOST_VDDK_SUPPORT = "host.vddk.support";
+ String HOST_VDDK_LIB_DIR = "vddk.lib.dir";
+ String HOST_VDDK_VERSION = "host.vddk.version";
+ String HOST_OVFTOOL_VERSION = "host.ovftool.version";
+ String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
+ String HOST_SSH_PORT = "host.ssh.port";
+ String HOST_CDROM_MAX_COUNT = "host.cdrom.max.count";
+ String GUEST_OS_CATEGORY_ID = "guest.os.category.id";
+ String GUEST_OS_RULE = "guest.os.rule";
+
+ int DEFAULT_SSH_PORT = 22;
/**
* @return name of the machine.
@@ -213,4 +225,6 @@ public static String[] toStrings(Host.Type... types) {
ResourceState getResourceState();
CPU.CPUArch getArch();
+
+ String getStorageAccessGroups();
}
diff --git a/api/src/main/java/com/cloud/host/HostStats.java b/api/src/main/java/com/cloud/host/HostStats.java
index d14794401fa3..0e72b5f2d9d0 100644
--- a/api/src/main/java/com/cloud/host/HostStats.java
+++ b/api/src/main/java/com/cloud/host/HostStats.java
@@ -36,5 +36,4 @@ public interface HostStats {
public HostStats getHostStats();
public double getLoadAverage();
- // public double getXapiMemoryUsageKBs();
}
diff --git a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java
index 27ffef1c3708..1f8741d3b7b2 100644
--- a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java
+++ b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java
@@ -31,20 +31,22 @@
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.DirectDownloadTemplate;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.RootDiskSizeOverride;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigration;
+import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigrationWithSnapshots;
public class Hypervisor {
public static class HypervisorType {
public enum Functionality {
DirectDownloadTemplate,
RootDiskSizeOverride,
- VmStorageMigration
+ VmStorageMigration,
+ VmStorageMigrationWithSnapshots
}
private static final Map hypervisorTypeMap = new LinkedHashMap<>();
public static final HypervisorType None = new HypervisorType("None"); //for storage hosts
public static final HypervisorType XenServer = new HypervisorType("XenServer", ImageFormat.VHD, EnumSet.of(RootDiskSizeOverride, VmStorageMigration));
public static final HypervisorType KVM = new HypervisorType("KVM", ImageFormat.QCOW2, EnumSet.of(DirectDownloadTemplate, RootDiskSizeOverride, VmStorageMigration));
- public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration));
+ public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration, VmStorageMigrationWithSnapshots));
public static final HypervisorType Hyperv = new HypervisorType("Hyperv");
public static final HypervisorType VirtualBox = new HypervisorType("VirtualBox");
public static final HypervisorType Parralels = new HypervisorType("Parralels");
@@ -54,6 +56,7 @@ public enum Functionality {
public static final HypervisorType Ovm3 = new HypervisorType("Ovm3", ImageFormat.RAW);
public static final HypervisorType LXC = new HypervisorType("LXC");
public static final HypervisorType Custom = new HypervisorType("Custom", null, EnumSet.of(RootDiskSizeOverride));
+ public static final HypervisorType External = new HypervisorType("External", null, EnumSet.of(RootDiskSizeOverride));
public static final HypervisorType Any = new HypervisorType("Any"); /*If you don't care about the hypervisor type*/
private final String name;
private final ImageFormat imageFormat;
diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
similarity index 76%
rename from plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
rename to api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
index 591da077aec6..80f6a6045c72 100644
--- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
+++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java
@@ -44,6 +44,8 @@ enum Event {
AutoscaleRequested,
ScaleUpRequested,
ScaleDownRequested,
+ AddNodeRequested,
+ RemoveNodeRequested,
UpgradeRequested,
OperationSucceeded,
OperationFailed,
@@ -58,7 +60,10 @@ enum State {
Stopping("Resources for the Kubernetes cluster are being destroyed"),
Stopped("All resources for the Kubernetes cluster are destroyed, Kubernetes cluster may still have ephemeral resource like persistent volumes provisioned"),
Scaling("Transient state in which resources are either getting scaled up/down"),
+ ScalingStoppedCluster("Transient state in which the service offerings of stopped clusters are getting scaled"),
Upgrading("Transient state in which cluster is getting upgraded"),
+ Importing("Transient state in which additional nodes are added as worker nodes to a cluster"),
+ RemovingNodes("Transient state in which additional nodes are removed from a cluster"),
Alert("State to represent Kubernetes clusters which are not in expected desired state (operationally in active control place, stopped cluster VM's etc)."),
Recovering("State in which Kubernetes cluster is recovering from alert state"),
Destroyed("End state of Kubernetes cluster in which all resources are destroyed, cluster will not be usable further"),
@@ -83,19 +88,35 @@ enum State {
s_fsm.addTransition(State.Stopping, Event.OperationFailed, State.Alert);
s_fsm.addTransition(State.Stopped, Event.StartRequested, State.Starting);
+ s_fsm.addTransition(State.Stopped, Event.OperationSucceeded, State.Stopped);
+ s_fsm.addTransition(State.Running, Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.Running, Event.FaultsDetected, State.Alert);
s_fsm.addTransition(State.Running, Event.AutoscaleRequested, State.Scaling);
s_fsm.addTransition(State.Running, Event.ScaleUpRequested, State.Scaling);
s_fsm.addTransition(State.Running, Event.ScaleDownRequested, State.Scaling);
+ s_fsm.addTransition(State.Stopped, Event.ScaleUpRequested, State.ScalingStoppedCluster);
s_fsm.addTransition(State.Scaling, Event.OperationSucceeded, State.Running);
- s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Alert);
+ s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Running);
+ s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationSucceeded, State.Stopped);
+ s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationFailed, State.Alert);
s_fsm.addTransition(State.Running, Event.UpgradeRequested, State.Upgrading);
s_fsm.addTransition(State.Upgrading, Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.Upgrading, Event.OperationFailed, State.Alert);
+ s_fsm.addTransition(State.Running, Event.AddNodeRequested, State.Importing);
+ s_fsm.addTransition(State.Alert, Event.AddNodeRequested, State.Importing);
+ s_fsm.addTransition(State.Importing, Event.OperationSucceeded, State.Running);
+ s_fsm.addTransition(State.Importing, Event.OperationFailed, State.Running);
+ s_fsm.addTransition(State.Alert, Event.OperationSucceeded, State.Running);
+
+ s_fsm.addTransition(State.Running, Event.RemoveNodeRequested, State.RemovingNodes);
+ s_fsm.addTransition(State.Alert, Event.RemoveNodeRequested, State.RemovingNodes);
+ s_fsm.addTransition(State.RemovingNodes, Event.OperationSucceeded, State.Running);
+ s_fsm.addTransition(State.RemovingNodes, Event.OperationFailed, State.Running);
+
s_fsm.addTransition(State.Alert, Event.RecoveryRequested, State.Recovering);
s_fsm.addTransition(State.Recovering, Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.Recovering, Event.OperationFailed, State.Alert);
@@ -142,4 +163,14 @@ enum State {
Long getMaxSize();
Long getSecurityGroupId();
ClusterType getClusterType();
+ Long getControlNodeServiceOfferingId();
+ Long getWorkerNodeServiceOfferingId();
+ Long getEtcdNodeServiceOfferingId();
+ Long getControlNodeTemplateId();
+ Long getWorkerNodeTemplateId();
+ Long getEtcdNodeTemplateId();
+ Long getEtcdNodeCount();
+ Long getCniConfigId();
+ String getCniConfigDetails();
+ boolean isCsiEnabled();
}
diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java
index a13c1b3a6a89..5a6eaa3f7b9a 100644
--- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java
+++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java
@@ -18,12 +18,26 @@
import org.apache.cloudstack.acl.ControlledEntity;
+import java.util.List;
+import java.util.Map;
+
+import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.utils.component.Adapter;
public interface KubernetesServiceHelper extends Adapter {
+ enum KubernetesClusterNodeType {
+ CONTROL, WORKER, ETCD, DEFAULT
+ }
+
ControlledEntity findByUuid(String uuid);
ControlledEntity findByVmId(long vmId);
void checkVmCanBeDestroyed(UserVm userVm);
+ void checkVmAffinityGroupsCanBeUpdated(UserVm userVm);
+ boolean isValidNodeType(String nodeType);
+ Map getServiceOfferingNodeTypeMap(Map> serviceOfferingNodeTypeMap);
+ Map getTemplateNodeTypeMap(Map> templateNodeTypeMap);
+ Map> getAffinityGroupNodeTypeMap(Map> affinityGroupNodeTypeMap);
+ void cleanupForAccount(Account account);
}
diff --git a/api/src/main/java/com/cloud/network/IpAddress.java b/api/src/main/java/com/cloud/network/IpAddress.java
index ae1af4505773..70d652b54e99 100644
--- a/api/src/main/java/com/cloud/network/IpAddress.java
+++ b/api/src/main/java/com/cloud/network/IpAddress.java
@@ -99,4 +99,5 @@ enum Purpose {
boolean isForSystemVms();
+ boolean isForRouter();
}
diff --git a/api/src/main/java/com/cloud/network/Ipv6Service.java b/api/src/main/java/com/cloud/network/Ipv6Service.java
index 4ef5f98c38d8..e6c3b9250a7a 100644
--- a/api/src/main/java/com/cloud/network/Ipv6Service.java
+++ b/api/src/main/java/com/cloud/network/Ipv6Service.java
@@ -45,7 +45,7 @@ public interface Ipv6Service extends PluggableService, Configurable {
static final ConfigKey Ipv6OfferingCreationEnabled = new ConfigKey("Advanced", Boolean.class,
"ipv6.offering.enabled",
"false",
- "Indicates whether creation of IPv6 network/VPC offering is enabled or not.",
+ "Indicates whether creation of IPv6 Network/VPC offering is enabled or not.",
true);
static final ConfigKey Ipv6PrefixSubnetCleanupInterval = new ConfigKey("Advanced", Integer.class,
diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java
index d3bc5005cb7a..e41eb880ffd5 100644
--- a/api/src/main/java/com/cloud/network/Network.java
+++ b/api/src/main/java/com/cloud/network/Network.java
@@ -206,6 +206,7 @@ public static class Provider {
public static final Provider Tungsten = new Provider("Tungsten", false);
public static final Provider Nsx = new Provider("Nsx", false);
+ public static final Provider Netris = new Provider("Netris", false);
private final String name;
private final boolean isExternal;
@@ -324,9 +325,9 @@ enum Event {
public enum State {
- Allocated("Indicates the network configuration is in allocated but not setup"), Setup("Indicates the network configuration is setup"), Implementing(
- "Indicates the network configuration is being implemented"), Implemented("Indicates the network configuration is in use"), Shutdown(
- "Indicates the network configuration is being destroyed"), Destroy("Indicates that the network is destroyed");
+ Allocated("Indicates the Network configuration is in allocated but not setup"), Setup("Indicates the Network configuration is setup"), Implementing(
+ "Indicates the Network configuration is being implemented"), Implemented("Indicates the Network configuration is in use"), Shutdown(
+ "Indicates the Network configuration is being destroyed"), Destroy("Indicates that the Network is destroyed");
protected static final StateMachine2 s_fsm = new StateMachine2();
@@ -509,4 +510,6 @@ public void setIp6Address(String ip6Address) {
Integer getPrivateMtu();
Integer getNetworkCidrSize();
+
+ boolean getKeepMacAddressOnPublicNic();
}
diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java
index a4cd87af0080..c212e6319eb4 100644
--- a/api/src/main/java/com/cloud/network/NetworkModel.java
+++ b/api/src/main/java/com/cloud/network/NetworkModel.java
@@ -125,6 +125,10 @@ public interface NetworkModel {
*/
String getNextAvailableMacAddressInNetwork(long networkConfigurationId) throws InsufficientAddressCapacityException;
+ String getUniqueMacAddress(long macAddress, long networkId, long datacenterId) throws InsufficientAddressCapacityException;
+
+ boolean isMACUnique(String mac, long networkId);
+
PublicIpAddress getPublicIpAddress(long ipAddressId);
List extends Vlan> listPodVlans(long podId);
@@ -305,6 +309,8 @@ public interface NetworkModel {
NicProfile getNicProfile(VirtualMachine vm, long networkId, String broadcastUri);
+ NicProfile getNicProfile(VirtualMachine vm, Nic nic, DataCenter dataCenter);
+
Set getAvailableIps(Network network, String requestedIp);
String getDomainNetworkDomain(long domainId, long zoneId);
@@ -362,4 +368,8 @@ List generateVmData(String userData, String userDataDetails, String se
boolean checkSecurityGroupSupportForNetwork(Account account, DataCenter zone, List networkIds,
List securityGroupsIds);
+
+ default long getMacIdentifier(Long dataCenterId) {
+ return 0;
+ }
}
diff --git a/api/src/main/java/com/cloud/network/NetworkProfile.java b/api/src/main/java/com/cloud/network/NetworkProfile.java
index 2e8efb489308..d690344a0e38 100644
--- a/api/src/main/java/com/cloud/network/NetworkProfile.java
+++ b/api/src/main/java/com/cloud/network/NetworkProfile.java
@@ -385,6 +385,11 @@ public Integer getNetworkCidrSize() {
return networkCidrSize;
}
+ @Override
+ public boolean getKeepMacAddressOnPublicNic() {
+ return true;
+ }
+
@Override
public String toString() {
return String.format("NetworkProfile %s",
diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java
index b8dd464b3655..c32bb711c0f2 100644
--- a/api/src/main/java/com/cloud/network/NetworkService.java
+++ b/api/src/main/java/com/cloud/network/NetworkService.java
@@ -19,7 +19,6 @@
import java.util.List;
import java.util.Map;
-import com.cloud.dc.DataCenter;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin;
import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd;
@@ -39,13 +38,16 @@
import org.apache.cloudstack.api.command.user.vm.ListNicsCmd;
import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse;
import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
+import com.cloud.agent.api.to.NicTO;
+import com.cloud.dc.DataCenter;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
-import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.Network.Service;
import com.cloud.network.Networks.TrafficType;
@@ -57,7 +59,6 @@
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.Nic;
import com.cloud.vm.NicSecondaryIp;
-import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
/**
* The NetworkService interface is the "public" api to entities that make requests to the orchestration engine
@@ -80,7 +81,7 @@ public interface NetworkService {
true, ConfigKey.Scope.Zone);
public static final ConfigKey AllowUsersToSpecifyVRMtu = new ConfigKey<>("Advanced", Boolean.class,
- "allow.end.users.to.specify.vr.mtu", "false", "Allow end users to specify VR MTU",
+ "allow.end.users.to.specify.vr.mtu", "false", "Allow end Users to specify VR MTU",
true, ConfigKey.Scope.Zone);
List extends Network> getIsolatedNetworksOwnedByAccountInZone(long zoneId, Account owner);
@@ -107,6 +108,10 @@ Network createGuestNetwork(long networkOfferingId, String name, String displayTe
PhysicalNetwork physicalNetwork, long zoneId, ControlledEntity.ACLType aclType) throws
InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException;
+ Network createGuestNetwork(long networkOfferingId, String name, String displayText, Account owner,
+ PhysicalNetwork physicalNetwork, long zoneId, ControlledEntity.ACLType aclType, Pair vrIfaceMTUs) throws
+ InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException;
+
Pair, Integer> searchForNetworks(ListNetworksCmd cmd);
boolean deleteNetwork(long networkId, boolean forced);
@@ -227,7 +232,7 @@ Network createPrivateNetwork(String networkName, String displayText, long physic
/**
* Requests an IP address for the guest NIC
*/
- NicSecondaryIp allocateSecondaryGuestIP(long nicId, IpAddresses requestedIpPair) throws InsufficientAddressCapacityException;
+ NicSecondaryIp allocateSecondaryGuestIP(long nicId, IpAddresses requestedIpPair, String description) throws InsufficientAddressCapacityException;
boolean releaseSecondaryIpFromNic(long ipAddressId);
@@ -268,4 +273,12 @@ Network createPrivateNetwork(String networkName, String displayText, long physic
InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId);
InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId);
List getInternalLoadBalancerElements();
+
+ boolean handleCksIsoOnNetworkVirtualRouter(Long virtualRouterId, boolean mount) throws ResourceUnavailableException;
+
+ IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress);
+
+ String getNicVlanValueForExternalVm(NicTO nic);
+
+ Long getPreferredNetworkIdForPublicIpRuleAssignment(IpAddress ip, Long networkId);
}
diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java
index dfa0ddb84cae..5f767686dc97 100644
--- a/api/src/main/java/com/cloud/network/Networks.java
+++ b/api/src/main/java/com/cloud/network/Networks.java
@@ -78,7 +78,7 @@ public URI toUri(T value) {
}
@Override
public String getValueFrom(URI uri) {
- return uri.getAuthority();
+ return uri == null ? null : uri.getAuthority();
}
},
Vswitch("vs", String.class), LinkLocal(null, null), Vnet("vnet", Long.class), Storage("storage", Integer.class), Lswitch("lswitch", String.class) {
@@ -96,7 +96,7 @@ public URI toUri(T value) {
*/
@Override
public String getValueFrom(URI uri) {
- return uri.getSchemeSpecificPart();
+ return uri == null ? null : uri.getSchemeSpecificPart();
}
},
Mido("mido", String.class), Pvlan("pvlan", String.class),
@@ -129,7 +129,8 @@ public URI toUri(T value) {
UnDecided(null, null),
OpenDaylight("opendaylight", String.class),
TUNGSTEN("tf", String.class),
- NSX("nsx", String.class);
+ NSX("nsx", String.class),
+ Netris("netris", String.class);
private final String scheme;
private final Class> type;
@@ -176,7 +177,7 @@ public URI toUri(T value) {
* @return the scheme as BroadcastDomainType
*/
public static BroadcastDomainType getSchemeValue(URI uri) {
- return toEnumValue(uri.getScheme());
+ return toEnumValue(uri == null ? null : uri.getScheme());
}
/**
@@ -190,7 +191,7 @@ public static BroadcastDomainType getTypeOf(String str) throws URISyntaxExceptio
if (com.cloud.dc.Vlan.UNTAGGED.equalsIgnoreCase(str)) {
return Native;
}
- return getSchemeValue(new URI(str));
+ return getSchemeValue(str == null ? null : new URI(str));
}
/**
@@ -219,7 +220,7 @@ public static BroadcastDomainType toEnumValue(String scheme) {
* @return the host part as String
*/
public String getValueFrom(URI uri) {
- return uri.getHost();
+ return uri == null ? null : uri.getHost();
}
/**
@@ -242,7 +243,7 @@ public static String getValue(URI uri) {
* @throws URISyntaxException the string is not even an uri
*/
public static String getValue(String uriString) throws URISyntaxException {
- return getValue(new URI(uriString));
+ return getValue(uriString == null ? null : new URI(uriString));
}
/**
diff --git a/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java b/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java
index 9676badb4e90..d3804cd29daf 100644
--- a/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java
+++ b/api/src/main/java/com/cloud/network/PhysicalNetworkTrafficType.java
@@ -41,4 +41,6 @@ public interface PhysicalNetworkTrafficType extends InternalIdentity, Identity {
String getHypervNetworkLabel();
String getOvm3NetworkLabel();
+
+ String getVlan();
}
diff --git a/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java b/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java
index eb65ae9088ec..22a46ce9ecdf 100644
--- a/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java
+++ b/api/src/main/java/com/cloud/network/RouterHealthCheckResult.java
@@ -26,7 +26,7 @@ public interface RouterHealthCheckResult {
String getCheckType();
- boolean getCheckResult();
+ VirtualNetworkApplianceService.RouterHealthStatus getCheckResult();
Date getLastUpdateTime();
diff --git a/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java
new file mode 100644
index 000000000000..a22db4287dcd
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java
@@ -0,0 +1,358 @@
+// 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 com.cloud.network;
+
+import java.util.List;
+
+public class SDNProviderNetworkRule {
+
+ protected long domainId;
+ protected long accountId;
+ protected long zoneId;
+ protected Long networkResourceId;
+ protected String networkResourceName;
+ protected boolean isVpcResource;
+ protected long vmId;
+ protected long ruleId;
+ protected String publicIp;
+ protected String vmIp;
+ protected String publicPort;
+ protected String privatePort;
+ protected String protocol;
+ protected String algorithm;
+ protected List sourceCidrList;
+ protected List destinationCidrList;
+ protected Integer icmpCode;
+
+ protected Integer icmpType;
+ protected String trafficType;
+ protected Network.Service service;
+
+ public long getDomainId() {
+ return domainId;
+ }
+
+ public void setDomainId(long domainId) {
+ this.domainId = domainId;
+ }
+
+ public long getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(long accountId) {
+ this.accountId = accountId;
+ }
+
+ public long getZoneId() {
+ return zoneId;
+ }
+
+ public void setZoneId(long zoneId) {
+ this.zoneId = zoneId;
+ }
+
+ public Long getNetworkResourceId() {
+ return networkResourceId;
+ }
+
+ public void setNetworkResourceId(Long networkResourceId) {
+ this.networkResourceId = networkResourceId;
+ }
+
+ public String getNetworkResourceName() {
+ return networkResourceName;
+ }
+
+ public void setNetworkResourceName(String networkResourceName) {
+ this.networkResourceName = networkResourceName;
+ }
+
+ public boolean isVpcResource() {
+ return isVpcResource;
+ }
+
+ public void setVpcResource(boolean vpcResource) {
+ isVpcResource = vpcResource;
+ }
+
+ public long getVmId() {
+ return vmId;
+ }
+
+ public void setVmId(long vmId) {
+ this.vmId = vmId;
+ }
+
+ public long getRuleId() {
+ return ruleId;
+ }
+
+ public void setRuleId(long ruleId) {
+ this.ruleId = ruleId;
+ }
+
+ public String getPublicIp() {
+ return publicIp;
+ }
+
+ public void setPublicIp(String publicIp) {
+ this.publicIp = publicIp;
+ }
+
+ public String getVmIp() {
+ return vmIp;
+ }
+
+ public void setVmIp(String vmIp) {
+ this.vmIp = vmIp;
+ }
+
+ public String getPublicPort() {
+ return publicPort;
+ }
+
+ public void setPublicPort(String publicPort) {
+ this.publicPort = publicPort;
+ }
+
+ public String getPrivatePort() {
+ return privatePort;
+ }
+
+ public void setPrivatePort(String privatePort) {
+ this.privatePort = privatePort;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public Network.Service getService() {
+ return service;
+ }
+
+ public void setService(Network.Service service) {
+ this.service = service;
+ }
+
+ public Integer getIcmpCode() {
+ return icmpCode;
+ }
+
+ public void setIcmpCode(Integer icmpCode) {
+ this.icmpCode = icmpCode;
+ }
+
+ public Integer getIcmpType() {
+ return icmpType;
+ }
+
+ public void setIcmpType(Integer icmpType) {
+ this.icmpType = icmpType;
+ }
+
+ public List getSourceCidrList() {
+ return sourceCidrList;
+ }
+
+ public void setSourceCidrList(List sourceCidrList) {
+ this.sourceCidrList = sourceCidrList;
+ }
+
+ public List getDestinationCidrList() {
+ return destinationCidrList;
+ }
+
+ public void setDestinationCidrList(List destinationCidrList) {
+ this.destinationCidrList = destinationCidrList;
+ }
+
+ public String getTrafficType() {
+ return trafficType;
+ }
+
+ public void setTrafficType(String trafficType) {
+ this.trafficType = trafficType;
+ }
+
+ public static class Builder {
+ public long domainId;
+ public long accountId;
+ public long zoneId;
+ public Long networkResourceId;
+ public String networkResourceName;
+ public boolean isVpcResource;
+ public long vmId;
+
+ public long ruleId;
+ public String publicIp;
+ public String vmIp;
+ public String publicPort;
+ public String privatePort;
+ public String protocol;
+ public String algorithm;
+ public List sourceCidrList;
+ public List destinationCidrList;
+ public String trafficType;
+ public Integer icmpType;
+ public Integer icmpCode;
+ public Network.Service service;
+
+ public Builder() {
+ // Default constructor
+ }
+
+ public Builder setDomainId(long domainId) {
+ this.domainId = domainId;
+ return this;
+ }
+
+ public Builder setAccountId(long accountId) {
+ this.accountId = accountId;
+ return this;
+ }
+
+ public Builder setZoneId(long zoneId) {
+ this.zoneId = zoneId;
+ return this;
+ }
+
+ public Builder setNetworkResourceId(Long networkResourceId) {
+ this.networkResourceId = networkResourceId;
+ return this;
+ }
+
+ public Builder setNetworkResourceName(String networkResourceName) {
+ this.networkResourceName = networkResourceName;
+ return this;
+ }
+
+ public Builder setVpcResource(boolean isVpcResource) {
+ this.isVpcResource = isVpcResource;
+ return this;
+ }
+
+
+ public Builder setVmId(long vmId) {
+ this.vmId = vmId;
+ return this;
+ }
+
+ public Builder setRuleId(long ruleId) {
+ this.ruleId = ruleId;
+ return this;
+ }
+
+ public Builder setPublicIp(String publicIp) {
+ this.publicIp = publicIp;
+ return this;
+ }
+
+ public Builder setVmIp(String vmIp) {
+ this.vmIp = vmIp;
+ return this;
+ }
+
+ public Builder setPublicPort(String publicPort) {
+ this.publicPort = publicPort;
+ return this;
+ }
+
+ public Builder setPrivatePort(String privatePort) {
+ this.privatePort = privatePort;
+ return this;
+ }
+
+ public Builder setProtocol(String protocol) {
+ this.protocol = protocol;
+ return this;
+ }
+
+ public Builder setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ return this;
+ }
+
+ public Builder setTrafficType(String trafficType) {
+ this.trafficType = trafficType;
+ return this;
+ }
+
+ public Builder setIcmpType(Integer icmpType) {
+ this.icmpType = icmpType;
+ return this;
+ }
+
+ public Builder setIcmpCode(Integer icmpCode) {
+ this.icmpCode = icmpCode;
+ return this;
+ }
+
+ public Builder setSourceCidrList(List sourceCidrList) {
+ this.sourceCidrList = sourceCidrList;
+ return this;
+ }
+
+ public Builder setDestinationCidrList(List destinationCidrList) {
+ this.destinationCidrList = destinationCidrList;
+ return this;
+ }
+
+ public Builder setService(Network.Service service) {
+ this.service = service;
+ return this;
+ }
+
+ public SDNProviderNetworkRule build() {
+ SDNProviderNetworkRule rule = new SDNProviderNetworkRule();
+ rule.setDomainId(this.domainId);
+ rule.setAccountId(this.accountId);
+ rule.setZoneId(this.zoneId);
+ rule.setNetworkResourceId(this.networkResourceId);
+ rule.setNetworkResourceName(this.networkResourceName);
+ rule.setVpcResource(this.isVpcResource);
+ rule.setVmId(this.vmId);
+ rule.setVmIp(this.vmIp);
+ rule.setPublicIp(this.publicIp);
+ rule.setPublicPort(this.publicPort);
+ rule.setPrivatePort(this.privatePort);
+ rule.setProtocol(this.protocol);
+ rule.setRuleId(this.ruleId);
+ rule.setAlgorithm(this.algorithm);
+ rule.setIcmpType(this.icmpType);
+ rule.setIcmpCode(this.icmpCode);
+ rule.setSourceCidrList(this.sourceCidrList);
+ rule.setDestinationCidrList(this.destinationCidrList);
+ rule.setTrafficType(this.trafficType);
+ rule.setService(service);
+ return rule;
+ }
+ }
+}
diff --git a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java
index 994df875f7d3..51036abe0609 100644
--- a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java
+++ b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java
@@ -24,7 +24,7 @@
public interface Site2SiteVpnConnection extends ControlledEntity, InternalIdentity, Displayable {
enum State {
- Pending, Connecting, Connected, Disconnected, Error,
+ Pending, Connecting, Connected, Disconnected, Error, Removed
}
@Override
diff --git a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java
index cb92739d2837..a60f1d49336a 100644
--- a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java
+++ b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java
@@ -87,4 +87,8 @@ void startRouterForHA(VirtualMachine vm, Map performRouterHealthChecks(long routerId);
void collectNetworkStatistics(T router, Nic nic);
+
+ enum RouterHealthStatus{
+ SUCCESS, FAILED, WARNING, UNKNOWN;
+ }
}
diff --git a/api/src/main/java/com/cloud/network/as/AutoScaleService.java b/api/src/main/java/com/cloud/network/as/AutoScaleService.java
index ceca4de68428..4aef10f8de9e 100644
--- a/api/src/main/java/com/cloud/network/as/AutoScaleService.java
+++ b/api/src/main/java/com/cloud/network/as/AutoScaleService.java
@@ -70,6 +70,8 @@ public interface AutoScaleService {
Counter createCounter(CreateCounterCmd cmd);
+ Counter getCounter(long counterId);
+
boolean deleteCounter(long counterId) throws ResourceInUseException;
List extends Counter> listCounters(ListCountersCmd cmd);
diff --git a/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java b/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java
index c265c1f36a0a..c05b3bda3db5 100644
--- a/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java
+++ b/api/src/main/java/com/cloud/network/as/AutoScaleVmGroup.java
@@ -43,7 +43,7 @@ public static State fromValue(String state) {
} else if (state.equalsIgnoreCase("scaling")) {
return SCALING;
} else {
- throw new IllegalArgumentException("Unexpected AutoScale VM group state : " + state);
+ throw new IllegalArgumentException("Unexpected AutoScale Instance group state : " + state);
}
}
}
diff --git a/api/src/main/java/com/cloud/network/element/NetworkElement.java b/api/src/main/java/com/cloud/network/element/NetworkElement.java
index fa67575edd35..cb0fc2fca981 100644
--- a/api/src/main/java/com/cloud/network/element/NetworkElement.java
+++ b/api/src/main/java/com/cloud/network/element/NetworkElement.java
@@ -23,6 +23,7 @@
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.IpAddress;
import com.cloud.network.Network;
import com.cloud.network.Network.Capability;
import com.cloud.network.Network.Provider;
@@ -87,6 +88,14 @@ boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, Deplo
boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException,
ResourceUnavailableException;
+ /**
+ * Release IP from the network provider if reserved
+ * @param ipAddress
+ */
+ default boolean releaseIp(IpAddress ipAddress) {
+ return true;
+ }
+
/**
* The network is being shutdown.
* @param network
diff --git a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
index e99bc2fd416b..8dcc8b6d0a47 100644
--- a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
+++ b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java
@@ -17,12 +17,40 @@
package com.cloud.network.element;
import java.util.List;
+import java.util.Objects;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
import com.cloud.network.rules.PortForwardingRule;
+import com.cloud.network.vpc.NetworkACLItem;
public interface PortForwardingServiceProvider extends NetworkElement, IpDeployingRequester {
+
+ static String getPublicPortRange(PortForwardingRule rule) {
+ return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
+ String.valueOf(rule.getSourcePortStart()) :
+ String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
+ }
+
+ static String getPrivatePFPortRange(PortForwardingRule rule) {
+ return rule.getDestinationPortStart() == rule.getDestinationPortEnd() ?
+ String.valueOf(rule.getDestinationPortStart()) :
+ String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd()));
+ }
+
+ static String getPrivatePortRange(FirewallRule rule) {
+ return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
+ String.valueOf(rule.getSourcePortStart()) :
+ String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
+ }
+
+ static String getPrivatePortRangeForACLRule(NetworkACLItem rule) {
+ return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ?
+ String.valueOf(rule.getSourcePortStart()) :
+ String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
+ }
+
/**
* Apply rules
* @param network
diff --git a/api/src/main/java/com/cloud/network/element/VpcProvider.java b/api/src/main/java/com/cloud/network/element/VpcProvider.java
index 6debd1fbc2d8..fe8c8f8612f7 100644
--- a/api/src/main/java/com/cloud/network/element/VpcProvider.java
+++ b/api/src/main/java/com/cloud/network/element/VpcProvider.java
@@ -55,4 +55,8 @@ boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context
boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List extends NetworkACLItem> rules) throws ResourceUnavailableException;
boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address);
+
+ default boolean updateVpc(Vpc vpc, String previousVpcName) {
+ return true;
+ }
}
diff --git a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java
index 7b81c75ed845..ced664e54a96 100644
--- a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java
+++ b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java
@@ -215,4 +215,8 @@ void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDe
default boolean isSlaacV6Only() {
return true;
}
+
+ default boolean update(Network network, String prevNetworkName) {
+ return true;
+ }
}
diff --git a/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java b/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java
index 46f17237e029..b7fe3b26761c 100644
--- a/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java
+++ b/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java
@@ -41,13 +41,23 @@
public interface LoadBalancingRulesService {
/**
* Create a load balancer rule from the given ipAddress/port to the given private port
+ * @param xId an existing UUID for this rule (for instance a device generated one)
+ * @param name
+ * @param description
+ * @param srcPortStart
+ * @param srcPortEnd
+ * @param defPortStart
+ * @param defPortEnd
+ * @param ipAddrId
+ * @param protocol
+ * @param algorithm
+ * @param networkId
+ * @param lbOwnerId
* @param openFirewall
- * TODO
- * @param forDisplay TODO
- * @param cmd
- * the command specifying the ip address, public port, protocol, private port, and algorithm
- *
+ * @param lbProtocol
+ * @param forDisplay
* @return the newly created LoadBalancerVO if successful, null otherwise
+ * @throws NetworkRuleConflictException
* @throws InsufficientAddressCapacityException
*/
LoadBalancer createPublicLoadBalancerRule(String xId, String name, String description, int srcPortStart, int srcPortEnd, int defPortStart, int defPortEnd,
@@ -98,7 +108,7 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri
/**
* Assign a virtual machine or list of virtual machines, or Map of to a load balancer.
*/
- boolean assignToLoadBalancer(long lbRuleId, List vmIds, Map> vmIdIpMap, boolean isAutoScaleVM);
+ boolean assignToLoadBalancer(long lbRuleId, List vmIds, Map> vmIdIpMap, Map vmIdNetworkMap, boolean isAutoScaleVM);
boolean assignSSLCertToLoadBalancerRule(Long lbRuleId, String certName, String publicCert, String privateKey);
@@ -106,7 +116,7 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri
boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException;
- boolean assignCertToLoadBalancer(long lbRuleId, Long certId);
+ boolean assignCertToLoadBalancer(long lbRuleId, Long certId, boolean isForced);
boolean removeCertFromLoadBalancer(long lbRuleId);
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java
new file mode 100644
index 000000000000..afc21f7f511b
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java
@@ -0,0 +1,41 @@
+// 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 com.cloud.network.netris;
+
+public class NetrisLbBackend {
+ private long vmId;
+ private String vmIp;
+ private int port;
+
+ public NetrisLbBackend(long vmId, String vmIp, int port) {
+ this.vmId = vmId;
+ this.vmIp = vmIp;
+ this.port = port;
+ }
+
+ public long getVmId() {
+ return vmId;
+ }
+
+ public String getVmIp() {
+ return vmIp;
+ }
+
+ public int getPort() {
+ return port;
+ }
+}
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
new file mode 100644
index 000000000000..211517ead491
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
@@ -0,0 +1,108 @@
+// 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 com.cloud.network.netris;
+
+import com.cloud.network.SDNProviderNetworkRule;
+
+
+import java.util.List;
+
+public class NetrisNetworkRule {
+ public enum NetrisRuleAction {
+ PERMIT, DENY
+ }
+
+ private SDNProviderNetworkRule baseRule;
+ private NetrisRuleAction aclAction;
+ private List lbBackends;
+ private String lbRuleName;
+ private String lbCidrList;
+ private String reason;
+
+ public NetrisNetworkRule(Builder builder) {
+ this.baseRule = builder.baseRule;
+ this.aclAction = builder.aclAction;
+ this.lbBackends = builder.lbBackends;
+ this.reason = builder.reason;
+ this.lbCidrList = builder.lbCidrList;
+ this.lbRuleName = builder.lbRuleName;
+ }
+
+ public NetrisRuleAction getAclAction() {
+ return aclAction;
+ }
+
+ public List getLbBackends() {
+ return lbBackends;
+ }
+
+ public String getReason() {
+ return reason;
+ }
+
+ public String getLbCidrList() {return lbCidrList; }
+
+ public String getLbRuleName() { return lbRuleName; }
+
+ public SDNProviderNetworkRule getBaseRule() {
+ return baseRule;
+ }
+
+ // Builder class extending the parent builder
+ public static class Builder {
+ private SDNProviderNetworkRule baseRule;
+ private NetrisRuleAction aclAction;
+ private List lbBackends;
+ private String reason;
+ private String lbCidrList;
+ private String lbRuleName;
+
+ public Builder baseRule(SDNProviderNetworkRule baseRule) {
+ this.baseRule = baseRule;
+ return this;
+ }
+
+ public Builder aclAction(NetrisRuleAction aclAction) {
+ this.aclAction = aclAction;
+ return this;
+ }
+
+ public Builder lbBackends(List lbBackends) {
+ this.lbBackends = lbBackends;
+ return this;
+ }
+
+ public Builder reason(String reason) {
+ this.reason = reason;
+ return this;
+ }
+
+ public Builder lbCidrList(String lbCidrList) {
+ this.lbCidrList = lbCidrList;
+ return this;
+ }
+
+ public Builder lbRuleName(String lbRuleName) {
+ this.lbRuleName = lbRuleName;
+ return this;
+ }
+
+ public NetrisNetworkRule build() {
+ return new NetrisNetworkRule(this);
+ }
+ }
+}
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
new file mode 100644
index 000000000000..fccf2930e976
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java
@@ -0,0 +1,30 @@
+// 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 com.cloud.network.netris;
+
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+public interface NetrisProvider extends InternalIdentity, Identity {
+ long getZoneId();
+ String getName();
+ String getUrl();
+ String getUsername();
+ String getSiteName();
+ String getTenantName();
+ String getNetrisTag();
+}
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java
new file mode 100644
index 000000000000..110e9f07105a
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -0,0 +1,310 @@
+// 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 com.cloud.network.netris;
+
+import com.cloud.network.IpAddress;
+import com.cloud.network.Network;
+import com.cloud.network.SDNProviderNetworkRule;
+import com.cloud.network.vpc.StaticRoute;
+import com.cloud.network.vpc.Vpc;
+
+import java.util.List;
+
+/**
+ * Interface for Netris Services that provides methods to manage VPCs, networks,
+ * NAT rules, network rules, and static routes in an SDN (Software Defined Networking) environment.
+ */
+
+public interface NetrisService {
+
+ /**
+ * Creates IPAM (IP Address Management) allocations for zone-level public ranges.
+ *
+ * @param zoneId the ID of the zone
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId);
+
+ /**
+ * Creates a VPC (Virtual Private Cloud) resource.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcId the ID of the VPC
+ * @param vpcName the name of the VPC
+ * @param sourceNatEnabled true if source NAT is enabled
+ * @param cidr the CIDR of the VPC
+ * @param isVpcNetwork true if it is a VPC network
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
+
+ /**
+ * Updates an existing VPC resource.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcId the ID of the VPC
+ * @param vpcName the new name of the VPC
+ * @param previousVpcName the previous name of the VPC
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean updateVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, String previousVpcName);
+
+ /**
+ * Deletes a VPC resource.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpc the VPC to delete
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
+
+ /**
+ * Creates a virtual network (vNet) resource.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcName the name of the VPC
+ * @param vpcId the ID of the VPC
+ * @param networkName the name of the network
+ * @param networkId the ID of the network
+ * @param cidr the CIDR of the network
+ * @param globalRouting true if global routing is enabled
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting);
+
+ /**
+ * Updates an existing vNet resource.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcName the name of the VPC
+ * @param vpcId the ID of the VPC
+ * @param networkName the new name of the network
+ * @param networkId the ID of the network
+ * @param prevNetworkName the previous name of the network
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean updateVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String prevNetworkName);
+
+ /**
+ * Deletes an existing vNet resource.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcName the name of the VPC
+ * @param vpcId the ID of the VPC
+ * @param networkName the name of the network
+ * @param networkId the ID of the network
+ * @param cidr the CIDR of the network
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
+
+ /**
+ * Creates a source NAT rule for a VPC or network.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcName the name of the VPC
+ * @param vpcId the ID of the VPC
+ * @param networkName the name of the network
+ * @param networkId the ID of the network
+ * @param isForVpc true if the rule applies to a VPC
+ * @param vpcCidr the VPC CIDR
+ * @param sourceNatIp the source NAT IP
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp);
+
+ /**
+ * Creates a port forwarding rule for a VPC or network.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcName the name of the VPC
+ * @param vpcId the ID of the VPC
+ * @param networkName the name of the network
+ * @param networkId the ID of the network
+ * @param isForVpc true if the rule applies to a VPC
+ * @param vpcCidr the VPC CIDR
+ * @param networkRule the network rule to forward
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule);
+
+ /**
+ * Deletes a port forwarding rule for a VPC or network.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param vpcName the name of the VPC
+ * @param vpcId the ID of the VPC
+ * @param networkName the name of the network
+ * @param networkId the ID of the network
+ * @param isForVpc true if the rule applies to a VPC
+ * @param vpcCidr the VPC CIDR
+ * @param networkRule the network rule to remove
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule);
+
+ /**
+ * Updates the source NAT IP for a specified VPC.
+ *
+ * @param vpc the VPC to updates
+ * @param address the new source NAT IP address
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address);
+
+ /**
+ * Creates a static NAT rule for a specific VM.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param networkResourceName the name of the network resource
+ * @param networkResourceId the ID of the network resource
+ * @param isForVpc true if the rule applies to a VPC
+ * @param vpcCidr the VPC CIDR
+ * @param staticNatIp the static NAT IP
+ * @param vmIp the VM's IP address
+ * @param vmId the ID of the VM
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp, long vmId);
+
+ /**
+ * Deletes a static NAT rule for a specific VM.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param networkResourceName the name of the network resource
+ * @param networkResourceId the ID of the network resource
+ * @param isForVpc true if the rule applies to a VPC
+ * @param staticNatIp the static NAT IP
+ * @param vmId the ID of the VM
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp, long vmId);
+
+ /**
+ * Adds firewall rules to a specific network.
+ *
+ * @param network the target network
+ * @param firewallRules the list of firewall rules to add
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean addFirewallRules(Network network, List firewallRules);
+
+ /**
+ * Deletes firewall rules from a specific network.
+ *
+ * @param network the target network
+ * @param firewallRules the list of firewall rules to delete
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean deleteFirewallRules(Network network, List firewallRules);
+
+ /**
+ * Adds or updates a static route for a specific network or VPC.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param networkResourceName the name of the network resource
+ * @param networkResourceId the ID of the network resource
+ * @param isForVpc true if it is for a VPC
+ * @param prefix the IP prefix of the route
+ * @param nextHop the next hop address
+ * @param routeId the ID of the route
+ * @param updateRoute true if the route should be updated
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute);
+
+ /**
+ * Deletes a specific static route for a network or VPC.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param networkResourceName the name of the network resource
+ * @param networkResourceId the ID of the network resource
+ * @param isForVpc true if it is for a VPC
+ * @param prefix the IP prefix of the route
+ * @param nextHop the next hop address
+ * @param routeId the ID of the route
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId);
+
+ /**
+ * Lists static routes for a specific network or VPC.
+ *
+ * @param zoneId the ID of the zone
+ * @param accountId the ID of the account
+ * @param domainId the ID of the domain
+ * @param networkResourceName the name of the network resource
+ * @param networkResourceId the ID of the network resource
+ * @param isForVpc true if it is for a VPC
+ * @param prefix the IP prefix of the route
+ * @param nextHop the next hop address
+ * @param routeId the ID of the route
+ * @return a list of static routes
+ */
+ List listStaticRoutes(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId);
+
+ /**
+ * Releases a NAT IP address.
+ *
+ * @param zoneId the ID of the zone
+ * @param publicIp the public NAT IP to release
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean releaseNatIp(long zoneId, String publicIp);
+
+ /**
+ * Creates or updates a load balancer (LB) rule.
+ *
+ * @param rule the network rule for the load balancer
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean createOrUpdateLbRule(NetrisNetworkRule rule);
+
+ /**
+ * Deletes a load balancer (LB) rule.
+ *
+ * @param rule the network rule to delete
+ * @return true if the operation is successful, false otherwise
+ */
+ boolean deleteLbRule(NetrisNetworkRule rule);
+}
diff --git a/api/src/main/java/com/cloud/network/nsx/NsxService.java b/api/src/main/java/com/cloud/network/nsx/NsxService.java
index bc4e6aafbfec..1adb7461cc09 100644
--- a/api/src/main/java/com/cloud/network/nsx/NsxService.java
+++ b/api/src/main/java/com/cloud/network/nsx/NsxService.java
@@ -16,9 +16,10 @@
// under the License.
package com.cloud.network.nsx;
+import org.apache.cloudstack.framework.config.ConfigKey;
+
import com.cloud.network.IpAddress;
import com.cloud.network.vpc.Vpc;
-import org.apache.cloudstack.framework.config.ConfigKey;
public interface NsxService {
@@ -33,4 +34,5 @@ public interface NsxService {
boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled);
boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address);
+ String getSegmentId(long domainId, long accountId, long zoneId, Long vpcId, long networkId);
}
diff --git a/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java b/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java
index 56a0622a52ba..5143611ee828 100644
--- a/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java
+++ b/api/src/main/java/com/cloud/network/rules/LbStickinessMethod.java
@@ -108,8 +108,7 @@ public LbStickinessMethod(StickinessMethodType methodType, String description) {
}
public void addParam(String name, Boolean required, String description, Boolean isFlag) {
- /* FIXME : UI is breaking if the capability string length is larger , temporarily description is commented out */
- // LbStickinessMethodParam param = new LbStickinessMethodParam(name, required, description);
+ /* is this still a valid comment: FIXME : UI is breaking if the capability string length is larger , temporarily description is commented out */
LbStickinessMethodParam param = new LbStickinessMethodParam(name, required, " ", isFlag);
_paramList.add(param);
return;
@@ -133,7 +132,6 @@ public String getDescription() {
public void setDescription(String description) {
/* FIXME : UI is breaking if the capability string length is larger , temporarily description is commented out */
- //this.description = description;
this._description = " ";
}
}
diff --git a/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java b/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java
index 40aee1f08f1d..84e48d5d5b8a 100644
--- a/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java
+++ b/api/src/main/java/com/cloud/network/vpc/NetworkACLService.java
@@ -19,6 +19,7 @@
import java.util.List;
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
+import org.apache.cloudstack.api.command.user.network.ImportNetworkACLCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworkACLListsCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd;
import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd;
@@ -98,4 +99,6 @@ public interface NetworkACLService {
NetworkACLItem moveNetworkAclRuleToNewPosition(MoveNetworkAclItemCmd moveNetworkAclItemCmd);
NetworkACLItem moveRuleToTheTopInACLList(NetworkACLItem ruleBeingMoved);
+
+ List importNetworkACLRules(ImportNetworkACLCmd cmd) throws ResourceUnavailableException;
}
diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
index 5707ca140246..739fca328b8c 100644
--- a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
+++ b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java
@@ -25,6 +25,7 @@ enum State {
Staged, // route been created but has never got through network rule conflict detection. Routes in this state can not be sent to VPC virtual router.
Add, // Add means the route has been created and has gone through network rule conflict detection.
Active, // Route has been sent to the VPC router and reported to be active.
+ Update,
Revoke, // Revoke means this route has been revoked. If this route has been sent to the VPC router, the route will be deleted from database.
Deleting // rule has been revoked and is scheduled for deletion
}
@@ -32,7 +33,9 @@ enum State {
/**
* @return
*/
- long getVpcGatewayId();
+ Long getVpcGatewayId();
+
+ String getNextHop();
/**
* @return
diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java
index cb4849f1f7b2..c8fc073911fe 100644
--- a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java
+++ b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java
@@ -23,7 +23,8 @@ public class StaticRouteProfile implements StaticRoute {
private String targetCidr;
private long accountId;
private long domainId;
- private long gatewayId;
+ private Long gatewayId;
+ private String nextHop;
private StaticRoute.State state;
private long vpcId;
String vlanTag;
@@ -46,6 +47,18 @@ public StaticRouteProfile(StaticRoute staticRoute, VpcGateway gateway) {
ipAddress = gateway.getIp4Address();
}
+ public StaticRouteProfile(StaticRoute staticRoute) {
+ id = staticRoute.getId();
+ uuid = staticRoute.getUuid();
+ targetCidr = staticRoute.getCidr();
+ accountId = staticRoute.getAccountId();
+ domainId = staticRoute.getDomainId();
+ gatewayId = staticRoute.getVpcGatewayId();
+ state = staticRoute.getState();
+ vpcId = staticRoute.getVpcId();
+ gateway = staticRoute.getNextHop();
+ }
+
@Override
public long getAccountId() {
return accountId;
@@ -57,10 +70,15 @@ public long getDomainId() {
}
@Override
- public long getVpcGatewayId() {
+ public Long getVpcGatewayId() {
return gatewayId;
}
+ @Override
+ public String getNextHop() {
+ return nextHop;
+ }
+
@Override
public String getCidr() {
return targetCidr;
diff --git a/api/src/main/java/com/cloud/network/vpc/Vpc.java b/api/src/main/java/com/cloud/network/vpc/Vpc.java
index e9a831c9d839..a0686e2bf7d0 100644
--- a/api/src/main/java/com/cloud/network/vpc/Vpc.java
+++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java
@@ -105,4 +105,8 @@ public enum State {
String getIp6Dns1();
String getIp6Dns2();
+
+ boolean useRouterIpAsResolver();
+
+ boolean getKeepMacAddressOnPublicNic();
}
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
index 38263f59667c..f84602232159 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java
@@ -32,6 +32,8 @@ public enum State {
public static final String redundantVPCOfferingName = "Redundant VPC offering";
public static final String DEFAULT_VPC_NAT_NSX_OFFERING_NAME = "VPC offering with NSX - NAT Mode";
public static final String DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME = "VPC offering with NSX - Route Mode";
+ public static final String DEFAULT_VPC_ROUTE_NETRIS_OFFERING_NAME = "VPC offering with Netris - Route Mode";
+ public static final String DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME = "VPC offering with Netris - NAT Mode";
/**
*
@@ -56,8 +58,6 @@ public enum State {
*/
boolean isDefault();
- boolean isForNsx();
-
NetworkOffering.NetworkMode getNetworkMode();
/**
@@ -84,4 +84,6 @@ public enum State {
NetworkOffering.RoutingMode getRoutingMode();
Boolean isSpecifyAsNumber();
+
+ boolean isConserveMode();
}
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java
index 10f1ddcc12d6..891cfb02d9df 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java
@@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
+import org.apache.cloudstack.api.command.admin.vpc.CloneVPCOfferingCmd;
import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd;
import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd;
import org.apache.cloudstack.api.command.user.vpc.ListVPCOfferingsCmd;
@@ -34,12 +35,14 @@ public interface VpcProvisioningService {
VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd);
+ VpcOffering cloneVPCOffering(CloneVPCOfferingCmd cmd);
+
VpcOffering createVpcOffering(String name, String displayText, List supportedServices,
Map> serviceProviders,
Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol,
- Long serviceOfferingId, Boolean forNsx, NetworkOffering.NetworkMode networkMode,
+ Long serviceOfferingId, String externalProvider, NetworkOffering.NetworkMode networkMode,
List domainIds, List zoneIds, VpcOffering.State state,
- NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber);
+ NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber, boolean conserveMode);
Pair,Integer> listVpcOfferings(ListVPCOfferingsCmd cmd);
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcService.java b/api/src/main/java/com/cloud/network/vpc/VpcService.java
index af2a9847a62d..3d0ba43263f5 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcService.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java
@@ -48,17 +48,17 @@ public interface VpcService {
* @param vpcName
* @param displayText
* @param cidr
- * @param networkDomain TODO
+ * @param networkDomain TODO
* @param ip4Dns1
* @param ip4Dns2
- * @param displayVpc TODO
+ * @param displayVpc TODO
+ * @param useVrIpResolver
* @return
* @throws ResourceAllocationException TODO
*/
Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain,
String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu, Integer cidrSize,
- Long asNumber, List bgpPeerIds)
- throws ResourceAllocationException;
+ Long asNumber, List bgpPeerIds, Boolean useVrIpResolver, boolean keepMacAddressOnPublicNic) throws ResourceAllocationException;
/**
* Persists VPC record in the database
@@ -104,7 +104,7 @@ Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, Strin
* @throws ResourceUnavailableException if during restart some resources may not be available
* @throws InsufficientCapacityException if for instance no address space, compute or storage is sufficiently available
*/
- Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu, String sourceNatIp) throws ResourceUnavailableException, InsufficientCapacityException;
+ Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu, String sourceNatIp, Boolean keepMacAddressOnPublicNic) throws ResourceUnavailableException, InsufficientCapacityException;
/**
* Lists VPC(s) based on the parameters passed to the API call
@@ -238,7 +238,7 @@ Pair, Integer> listVpcs(Long id, String vpcName, String disp
* @param cidr
* @return
*/
- StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException;
+ StaticRoute createStaticRoute(Long gatewayId, Long vpcId, String nextHop, String cidr) throws NetworkRuleConflictException;
/**
* Lists static routes based on parameters passed to the call
diff --git a/api/src/main/java/com/cloud/offering/DiskOffering.java b/api/src/main/java/com/cloud/offering/DiskOffering.java
index e1c41f77cbf5..d74f5703cc99 100644
--- a/api/src/main/java/com/cloud/offering/DiskOffering.java
+++ b/api/src/main/java/com/cloud/offering/DiskOffering.java
@@ -37,7 +37,7 @@ enum State {
State getState();
enum DiskCacheMode {
- NONE("none"), WRITEBACK("writeback"), WRITETHROUGH("writethrough");
+ NONE("none"), WRITEBACK("writeback"), WRITETHROUGH("writethrough"), HYPERVISOR_DEFAULT("hypervisor_default");
private final String _diskCacheMode;
@@ -69,6 +69,8 @@ public String toString() {
boolean isCustomized();
+ boolean isShared();
+
void setDiskSize(long diskSize);
long getDiskSize();
@@ -99,7 +101,6 @@ public String toString() {
Long getBytesReadRateMaxLength();
-
void setBytesWriteRate(Long bytesWriteRate);
Long getBytesWriteRate();
@@ -112,7 +113,6 @@ public String toString() {
Long getBytesWriteRateMaxLength();
-
void setIopsReadRate(Long iopsReadRate);
Long getIopsReadRate();
@@ -133,7 +133,6 @@ public String toString() {
Long getIopsWriteRateMax();
-
void setIopsWriteRateMaxLength(Long iopsWriteRateMaxLength);
Long getIopsWriteRateMaxLength();
diff --git a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java
index d83039e15c2b..197565a1fccb 100644
--- a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java
+++ b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java
@@ -23,6 +23,7 @@ public class DiskOfferingInfo {
private Long _size;
private Long _minIops;
private Long _maxIops;
+ private Long _kmsKeyId;
public DiskOfferingInfo() {
}
@@ -31,6 +32,21 @@ public DiskOfferingInfo(DiskOffering diskOffering) {
_diskOffering = diskOffering;
}
+ public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops) {
+ _diskOffering = diskOffering;
+ _size = size;
+ _minIops = minIops;
+ _maxIops = maxIops;
+ }
+
+ public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long kmsKeyId) {
+ _diskOffering = diskOffering;
+ _size = size;
+ _minIops = minIops;
+ _maxIops = maxIops;
+ _kmsKeyId = kmsKeyId;
+ }
+
public void setDiskOffering(DiskOffering diskOffering) {
_diskOffering = diskOffering;
}
@@ -62,4 +78,12 @@ public void setMaxIops(Long maxIops) {
public Long getMaxIops() {
return _maxIops;
}
+
+ public void setKmsKeyId(Long kmsKeyId) {
+ _kmsKeyId = kmsKeyId;
+ }
+
+ public Long getKmsKeyId() {
+ return _kmsKeyId;
+ }
}
diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java
index 7011aea679ee..5000a4f8c626 100644
--- a/api/src/main/java/com/cloud/offering/NetworkOffering.java
+++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java
@@ -64,6 +64,8 @@ enum RoutingMode {
public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC = "DefaultNATNSXNetworkOfferingForVpc";
public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB = "DefaultNATNSXNetworkOfferingForVpcWithInternalLB";
public static final String DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC = "DefaultRoutedNSXNetworkOfferingForVpc";
+ public static final String DEFAULT_ROUTED_NETRIS_OFFERING_FOR_VPC = "DefaultRoutedNetrisNetworkOfferingForVpc";
+ public static final String DEFAULT_NAT_NETRIS_OFFERING_FOR_VPC = "DefaultNATNetrisNetworkOfferingForVpc";
public static final String DEFAULT_NAT_NSX_OFFERING = "DefaultNATNSXNetworkOffering";
public static final String DEFAULT_ROUTED_NSX_OFFERING = "DefaultRoutedNSXNetworkOffering";
public final static String QuickCloudNoServices = "QuickCloudNoServices";
@@ -102,10 +104,6 @@ enum RoutingMode {
boolean isForVpc();
- boolean isForTungsten();
-
- boolean isForNsx();
-
NetworkMode getNetworkMode();
TrafficType getTrafficType();
diff --git a/api/src/main/java/com/cloud/offering/ServiceOffering.java b/api/src/main/java/com/cloud/offering/ServiceOffering.java
index acb7a9f1cf91..532123e4373a 100644
--- a/api/src/main/java/com/cloud/offering/ServiceOffering.java
+++ b/api/src/main/java/com/cloud/offering/ServiceOffering.java
@@ -142,4 +142,8 @@ enum StorageType {
Boolean getDiskOfferingStrictness();
void setDiskOfferingStrictness(boolean diskOfferingStrictness);
+
+ Long getVgpuProfileId();
+
+ Integer getGpuCount();
}
diff --git a/api/src/main/java/com/cloud/org/Cluster.java b/api/src/main/java/com/cloud/org/Cluster.java
index 5124168084c6..b0aa6bb04cf2 100644
--- a/api/src/main/java/com/cloud/org/Cluster.java
+++ b/api/src/main/java/com/cloud/org/Cluster.java
@@ -41,4 +41,6 @@ public static enum ClusterType {
ManagedState getManagedState();
CPU.CPUArch getArch();
+
+ String getStorageAccessGroups();
}
diff --git a/api/src/main/java/com/cloud/projects/ProjectService.java b/api/src/main/java/com/cloud/projects/ProjectService.java
index 5080cb5a7812..d11e9ae0446d 100644
--- a/api/src/main/java/com/cloud/projects/ProjectService.java
+++ b/api/src/main/java/com/cloud/projects/ProjectService.java
@@ -82,7 +82,7 @@ public interface ProjectService {
Project updateProject(long id, String name, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException;
- boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType);
+ boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException;
boolean deleteAccountFromProject(long projectId, String accountName);
@@ -100,6 +100,6 @@ public interface ProjectService {
Project findByProjectAccountIdIncludingRemoved(long projectAccountId);
- boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole);
+ boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException;
}
diff --git a/api/src/main/java/com/cloud/resource/ResourceService.java b/api/src/main/java/com/cloud/resource/ResourceService.java
index 562c3c418df1..3cdf8fc64e99 100644
--- a/api/src/main/java/com/cloud/resource/ResourceService.java
+++ b/api/src/main/java/com/cloud/resource/ResourceService.java
@@ -95,4 +95,11 @@ public interface ResourceService {
boolean releaseHostReservation(Long hostId);
+ void updatePodStorageAccessGroups(long podId, List newStorageAccessGroups);
+
+ void updateZoneStorageAccessGroups(long zoneId, List newStorageAccessGroups);
+
+ void updateClusterStorageAccessGroups(Long clusterId, List newStorageAccessGroups);
+
+ void updateHostStorageAccessGroups(Long hostId, List newStorageAccessGroups);
}
diff --git a/api/src/main/java/com/cloud/resource/ResourceState.java b/api/src/main/java/com/cloud/resource/ResourceState.java
index 70738c7921bc..e91cf820b081 100644
--- a/api/src/main/java/com/cloud/resource/ResourceState.java
+++ b/api/src/main/java/com/cloud/resource/ResourceState.java
@@ -76,6 +76,10 @@ public static Event toEvent(String e) {
}
}
+ public static List s_maintenanceStates = List.of(ResourceState.Maintenance,
+ ResourceState.ErrorInMaintenance, ResourceState.PrepareForMaintenance,
+ ResourceState.ErrorInPrepareForMaintenance);
+
public ResourceState getNextState(Event a) {
return s_fsm.getNextState(this, a);
}
@@ -98,8 +102,7 @@ public static String[] toString(ResourceState... states) {
}
public static boolean isMaintenanceState(ResourceState state) {
- return Arrays.asList(ResourceState.Maintenance, ResourceState.ErrorInMaintenance,
- ResourceState.PrepareForMaintenance, ResourceState.ErrorInPrepareForMaintenance).contains(state);
+ return s_maintenanceStates.contains(state);
}
public static boolean canAttemptMaintenance(ResourceState state) {
diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java
index 18f3e901cd93..bcca229c06bc 100644
--- a/api/src/main/java/com/cloud/server/ManagementService.java
+++ b/api/src/main/java/com/cloud/server/ManagementService.java
@@ -20,21 +20,24 @@
import java.util.List;
import java.util.Map;
-import com.cloud.user.UserData;
import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd;
+import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCategoryCmd;
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd;
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd;
+import org.apache.cloudstack.api.command.admin.guest.DeleteGuestOsCategoryCmd;
import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd;
import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd;
import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd;
import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd;
+import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCategoryCmd;
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd;
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd;
import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd;
+import org.apache.cloudstack.api.command.admin.management.RemoveManagementServerCmd;
import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd;
import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd;
import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd;
@@ -59,8 +62,10 @@
import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd;
import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd;
import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd;
+import org.apache.cloudstack.api.command.user.userdata.DeleteCniConfigurationCmd;
import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd;
import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd;
+import org.apache.cloudstack.api.command.user.userdata.RegisterCniConfigurationCmd;
import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd;
import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd;
@@ -71,6 +76,8 @@
import com.cloud.capacity.Capacity;
import com.cloud.dc.Pod;
import com.cloud.dc.Vlan;
+import com.cloud.deploy.DeploymentPlan;
+import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.ManagementServerException;
import com.cloud.exception.ResourceUnavailableException;
@@ -85,11 +92,13 @@
import com.cloud.storage.GuestOsCategory;
import com.cloud.storage.StoragePool;
import com.cloud.user.SSHKeyPair;
+import com.cloud.user.UserData;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.vm.InstanceGroup;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.Type;
+import com.cloud.vm.VirtualMachineProfile;
/**
* Hopefully this is temporary.
@@ -168,6 +177,12 @@ public interface ManagementService {
*/
Pair, Integer> listGuestOSCategoriesByCriteria(ListGuestOsCategoriesCmd cmd);
+ GuestOsCategory addGuestOsCategory(AddGuestOsCategoryCmd cmd);
+
+ GuestOsCategory updateGuestOsCategory(UpdateGuestOsCategoryCmd cmd);
+
+ boolean deleteGuestOsCategory(DeleteGuestOsCategoryCmd cmd);
+
/**
* Obtains a list of all guest OS mappings
*
@@ -360,17 +375,23 @@ public interface ManagementService {
* The api command class.
* @return The list of userdatas found.
*/
- Pair, Integer> listUserDatas(ListUserDataCmd cmd);
+ Pair, Integer> listUserDatas(ListUserDataCmd cmd, boolean forCks);
+
+ /**
+ * Registers a cni configuration.
+ *
+ * @param cmd The api command class.
+ * @return A VO with the registered user data.
+ */
+ UserData registerCniConfiguration(RegisterCniConfigurationCmd cmd);
/**
* Registers a userdata.
*
- * @param cmd
- * The api command class.
+ * @param cmd The api command class.
* @return A VO with the registered userdata.
*/
UserData registerUserData(RegisterUserDataCmd cmd);
-
/**
* Deletes a userdata.
*
@@ -380,6 +401,14 @@ public interface ManagementService {
*/
boolean deleteUserData(DeleteUserDataCmd cmd);
+ /**
+ * Deletes user data.
+ *
+ * @param cmd
+ * The api command class.
+ * @return True on success. False otherwise.
+ */
+ boolean deleteCniConfiguration(DeleteCniConfigurationCmd cmd);
/**
* Search registered key pairs for the logged in user.
*
@@ -443,6 +472,19 @@ public interface ManagementService {
Ternary, Integer>, List extends Host>, Map> listHostsForMigrationOfVM(VirtualMachine vm, Long startIndex, Long pageSize, String keyword, List vmList);
+ /**
+ * Apply affinity group constraints and other exclusion rules for VM migration.
+ * This is a helper method that can be used independently for per-iteration affinity checks in DRS.
+ *
+ * @param vm The virtual machine to migrate
+ * @param vmProfile The VM profile
+ * @param plan The deployment plan
+ * @param vmList List of VMs with current/simulated placements for affinity processing
+ * @return ExcludeList containing hosts to avoid
+ */
+ ExcludeList applyAffinityConstraints(VirtualMachine vm, VirtualMachineProfile vmProfile,
+ DeploymentPlan plan, List vmList);
+
/**
* List storage pools for live migrating of a volume. The API returns list of all pools in the cluster to which the
* volume can be migrated. Current pool is not included in the list. In case of vSphere datastore cluster storage pools,
@@ -481,4 +523,6 @@ VirtualMachine upgradeSystemVM(ScaleSystemVMCmd cmd) throws ResourceUnavailableE
Pair patchSystemVM(PatchSystemVMCmd cmd);
+ boolean removeManagementServer(RemoveManagementServerCmd cmd);
+
}
diff --git a/api/src/main/java/com/cloud/server/ResourceIconManager.java b/api/src/main/java/com/cloud/server/ResourceIconManager.java
index e5111d9160b8..d10b3eb0cd5b 100644
--- a/api/src/main/java/com/cloud/server/ResourceIconManager.java
+++ b/api/src/main/java/com/cloud/server/ResourceIconManager.java
@@ -16,7 +16,9 @@
// under the License.
package com.cloud.server;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
public interface ResourceIconManager {
@@ -25,4 +27,8 @@ public interface ResourceIconManager {
boolean deleteResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType);
ResourceIcon getByResourceTypeAndUuid(ResourceTag.ResourceObjectType type, String resourceId);
+
+ Map getByResourceTypeAndIds(ResourceTag.ResourceObjectType type, Collection resourceIds);
+
+ Map getByResourceTypeAndUuids(ResourceTag.ResourceObjectType type, Collection resourceUuids);
}
diff --git a/api/src/main/java/com/cloud/server/ResourceManagerUtil.java b/api/src/main/java/com/cloud/server/ResourceManagerUtil.java
index 9a3b51a70d56..f5081cbe307b 100644
--- a/api/src/main/java/com/cloud/server/ResourceManagerUtil.java
+++ b/api/src/main/java/com/cloud/server/ResourceManagerUtil.java
@@ -18,6 +18,7 @@
public interface ResourceManagerUtil {
long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType);
+ long getResourceId(String resourceId, ResourceTag.ResourceObjectType resourceType, boolean checkAccess);
String getUuid(String resourceId, ResourceTag.ResourceObjectType resourceType);
ResourceTag.ResourceObjectType getResourceType(String resourceTypeStr);
void checkResourceAccessible(Long accountId, Long domainId, String exceptionMessage);
diff --git a/api/src/main/java/com/cloud/server/ResourceTag.java b/api/src/main/java/com/cloud/server/ResourceTag.java
index 9bbb5d43eaeb..32305753f1ae 100644
--- a/api/src/main/java/com/cloud/server/ResourceTag.java
+++ b/api/src/main/java/com/cloud/server/ResourceTag.java
@@ -16,14 +16,14 @@
// under the License.
package com.cloud.server;
-import org.apache.cloudstack.acl.ControlledEntity;
-import org.apache.cloudstack.api.Identity;
-import org.apache.cloudstack.api.InternalIdentity;
-
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
public interface ResourceTag extends ControlledEntity, Identity, InternalIdentity {
// FIXME - extract enum to another interface as its used both by resourceTags and resourceMetaData code
@@ -66,10 +66,11 @@ public enum ResourceObjectType {
LBStickinessPolicy(false, true),
LBHealthCheckPolicy(false, true),
SnapshotPolicy(true, true),
+ GuestOsCategory(false, false, true),
GuestOs(false, true),
NetworkOffering(false, true),
VpcOffering(true, false),
- Domain(false, false, true),
+ Domain(true, false, true),
ObjectStore(false, false, true);
diff --git a/api/src/main/java/com/cloud/storage/GuestOsCategory.java b/api/src/main/java/com/cloud/storage/GuestOsCategory.java
index b46418d5c8f9..e1ee44891582 100644
--- a/api/src/main/java/com/cloud/storage/GuestOsCategory.java
+++ b/api/src/main/java/com/cloud/storage/GuestOsCategory.java
@@ -16,6 +16,8 @@
// under the License.
package com.cloud.storage;
+import java.util.Date;
+
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
@@ -27,4 +29,7 @@ public interface GuestOsCategory extends Identity, InternalIdentity {
void setName(String name);
+ boolean isFeatured();
+
+ Date getCreated();
}
diff --git a/api/src/main/java/com/cloud/storage/MigrationOptions.java b/api/src/main/java/com/cloud/storage/MigrationOptions.java
index a39a2a7c8272..8b642d09e29e 100644
--- a/api/src/main/java/com/cloud/storage/MigrationOptions.java
+++ b/api/src/main/java/com/cloud/storage/MigrationOptions.java
@@ -24,6 +24,7 @@ public class MigrationOptions implements Serializable {
private String srcPoolUuid;
private Storage.StoragePoolType srcPoolType;
+ private Long srcPoolClusterId;
private Type type;
private ScopeType scopeType;
private String srcBackingFilePath;
@@ -38,21 +39,23 @@ public enum Type {
public MigrationOptions() {
}
- public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType) {
+ public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType, Long srcPoolClusterId) {
this.srcPoolUuid = srcPoolUuid;
this.srcPoolType = srcPoolType;
this.type = Type.LinkedClone;
this.scopeType = scopeType;
this.srcBackingFilePath = srcBackingFilePath;
this.copySrcTemplate = copySrcTemplate;
+ this.srcPoolClusterId = srcPoolClusterId;
}
- public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType) {
+ public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType, Long srcPoolClusterId) {
this.srcPoolUuid = srcPoolUuid;
this.srcPoolType = srcPoolType;
this.type = Type.FullClone;
this.scopeType = scopeType;
this.srcVolumeUuid = srcVolumeUuid;
+ this.srcPoolClusterId = srcPoolClusterId;
}
public String getSrcPoolUuid() {
@@ -63,6 +66,10 @@ public Storage.StoragePoolType getSrcPoolType() {
return srcPoolType;
}
+ public Long getSrcPoolClusterId() {
+ return srcPoolClusterId;
+ }
+
public ScopeType getScopeType() { return scopeType; }
public String getSrcBackingFilePath() {
diff --git a/api/src/main/java/com/cloud/storage/Snapshot.java b/api/src/main/java/com/cloud/storage/Snapshot.java
index fc919e442b2e..c0a7b812ed9e 100644
--- a/api/src/main/java/com/cloud/storage/Snapshot.java
+++ b/api/src/main/java/com/cloud/storage/Snapshot.java
@@ -48,7 +48,7 @@ public boolean equals(String snapshotType) {
}
public enum State {
- Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed,
+ Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed, Hidden,
//it's a state, user can't see the snapshot from ui, while the snapshot may still exist on the storage
Error;
diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java
index 05b8b3ab7a86..ddf5978497ba 100644
--- a/api/src/main/java/com/cloud/storage/Storage.java
+++ b/api/src/main/java/com/cloud/storage/Storage.java
@@ -30,6 +30,7 @@ public static enum ImageFormat {
OVA(true, true, true, "ova"),
VHDX(true, true, true, "vhdx"),
BAREMETAL(false, false, false, "BAREMETAL"),
+ EXTERNAL(false, false, false, "EXTERNAL"),
VMDK(true, true, false, "vmdk"),
VDI(true, true, false, "vdi"),
TAR(false, false, false, "tar"),
@@ -127,7 +128,7 @@ public static enum FileSystem {
public static enum TemplateType {
ROUTING, // Router template
SYSTEM, /* routing, system vm template */
- BUILTIN, /* buildin template */
+ BUILTIN, /* builtin template */
PERHOST, /* every host has this template, don't need to install it in secondary storage */
USER, /* User supplied template/iso */
VNF, /* VNFs (virtual network functions) template */
@@ -169,6 +170,7 @@ public static enum StoragePoolType {
ISO(false, false, EncryptionSupport.Unsupported), // for iso image
LVM(false, false, EncryptionSupport.Unsupported), // XenServer local LVM SR
CLVM(true, false, EncryptionSupport.Unsupported),
+ CLVM_NG(true, false, EncryptionSupport.Hypervisor),
RBD(true, true, EncryptionSupport.Unsupported), // http://libvirt.org/storage.html#StorageBackendRBD
SharedMountPoint(true, true, EncryptionSupport.Hypervisor),
VMFS(true, true, EncryptionSupport.Unsupported), // VMware VMFS storage
diff --git a/api/src/main/java/com/cloud/storage/StorageService.java b/api/src/main/java/com/cloud/storage/StorageService.java
index b8df75cd3e4c..a29c8f6aecef 100644
--- a/api/src/main/java/com/cloud/storage/StorageService.java
+++ b/api/src/main/java/com/cloud/storage/StorageService.java
@@ -22,6 +22,7 @@
import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd;
import org.apache.cloudstack.api.command.admin.storage.ChangeStoragePoolScopeCmd;
+import org.apache.cloudstack.api.command.admin.storage.ConfigureStorageAccessCmd;
import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd;
import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd;
import org.apache.cloudstack.api.command.admin.storage.DeleteImageStoreCmd;
@@ -99,6 +100,8 @@ public interface StorageService {
StoragePool disablePrimaryStoragePool(Long id);
+ boolean configureStorageAccess(ConfigureStorageAccessCmd cmd);
+
StoragePool getStoragePool(long id);
boolean deleteImageStore(DeleteImageStoreCmd cmd);
@@ -131,7 +134,7 @@ public interface StorageService {
void removeSecondaryStorageHeuristic(RemoveSecondaryStorageSelectorCmd cmd);
- ObjectStore discoverObjectStore(String name, String url, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
+ ObjectStore discoverObjectStore(String name, String url, Long size, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
boolean deleteObjectStore(DeleteObjectStoragePoolCmd cmd);
diff --git a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java
index f43d5331222f..7d5b2d7c57d7 100644
--- a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java
+++ b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java
@@ -17,14 +17,18 @@
package com.cloud.storage;
import java.util.Date;
+import java.util.List;
import org.apache.cloudstack.api.InternalIdentity;
public interface VMTemplateStorageResourceAssoc extends InternalIdentity {
public static enum Status {
- UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED
+ UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, LIMIT_REACHED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED
}
+ List ERROR_DOWNLOAD_STATES = List.of(Status.DOWNLOAD_ERROR, Status.ABANDONED, Status.LIMIT_REACHED, Status.UNKNOWN);
+ List PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS);
+
String getInstallPath();
long getTemplateId();
diff --git a/api/src/main/java/com/cloud/storage/Volume.java b/api/src/main/java/com/cloud/storage/Volume.java
index c7fbdb0a5445..c7a13d5780d0 100644
--- a/api/src/main/java/com/cloud/storage/Volume.java
+++ b/api/src/main/java/com/cloud/storage/Volume.java
@@ -275,6 +275,14 @@ enum Event {
void setPassphraseId(Long id);
+ Long getKmsKeyId();
+
+ void setKmsKeyId(Long id);
+
+ Long getKmsWrappedKeyId();
+
+ void setKmsWrappedKeyId(Long id);
+
String getEncryptFormat();
void setEncryptFormat(String encryptFormat);
diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java
index 3a72f8bddc4c..d287cc335eed 100644
--- a/api/src/main/java/com/cloud/storage/VolumeApiService.java
+++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java
@@ -22,7 +22,13 @@
import java.util.List;
import java.util.Map;
+import com.cloud.dc.DataCenter;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.offering.DiskOffering;
+import com.cloud.user.Account;
import com.cloud.utils.Pair;
+import com.cloud.utils.fsm.NoTransitionException;
+
import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd;
@@ -37,13 +43,9 @@
import org.apache.cloudstack.api.response.GetUploadParamsResponse;
import org.apache.cloudstack.framework.config.ConfigKey;
-import com.cloud.exception.ResourceAllocationException;
-import com.cloud.user.Account;
-import com.cloud.utils.fsm.NoTransitionException;
-
public interface VolumeApiService {
- ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey("Advanced"
+ ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey<>("Advanced"
, Long.class
, "concurrent.migrations.per.target.datastore"
, "0"
@@ -51,13 +53,13 @@ public interface VolumeApiService {
, true // not sure if this is to be dynamic
, ConfigKey.Scope.Global);
- ConfigKey UseHttpsToUpload = new ConfigKey("Advanced",
+ ConfigKey UseHttpsToUpload = new ConfigKey<>("Advanced",
Boolean.class,
"use.https.to.upload",
"true",
- "Determines the protocol (HTTPS or HTTP) ACS will use to generate links to upload ISOs, volumes, and templates. When set as 'true', ACS will use protocol HTTPS, otherwise, it will use protocol HTTP. Default value is 'true'.",
+ "Controls whether upload links for ISOs, volumes, and templates use HTTPS (true, default) or HTTP (false). After changing this setting, the Secondary Storage VM (SSVM) must be recreated",
true,
- ConfigKey.Scope.StoragePool);
+ ConfigKey.Scope.Zone);
/**
* Creates the database object for a volume based on the given criteria
@@ -69,6 +71,10 @@ public interface VolumeApiService {
*/
Volume allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationException;
+ Volume allocVolume(long ownerId, Long zoneId, Long diskOfferingId, Long vmId, Long snapshotId, String name,
+ Long cmdSize, Boolean displayVolume, Long cmdMinIops, Long cmdMaxIops, String customId, Long kmsKeyId)
+ throws ResourceAllocationException;
+
/**
* Creates the volume based on the given criteria
*
@@ -79,13 +85,15 @@ public interface VolumeApiService {
*/
Volume createVolume(CreateVolumeCmd cmd);
+ Volume createVolume(long volumeId, Long vmId, Long snapshotId, Long storageId, Boolean display);
+
/**
* Resizes the volume based on the given criteria
*
* @param cmd
* the API command wrapping the criteria
* @return the volume object
- * @throws ResourceAllocationException
+ * @throws ResourceAllocationException no capacity to allocate the new volume size
*/
Volume resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationException;
@@ -112,10 +120,10 @@ public interface VolumeApiService {
Volume detachVolumeFromVM(DetachVolumeCmd cmd);
- Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map tags, List zoneIds)
+ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map tags, List zoneIds, List poolIds, Boolean useStorageReplication)
throws ResourceAllocationException;
- Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds) throws ResourceAllocationException;
+ Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds, List storagePoolIds, Boolean useStorageReplication) throws ResourceAllocationException;
Volume updateVolume(long volumeId, String path, String state, Long storageId,
Boolean displayVolume, Boolean deleteProtection,
@@ -136,16 +144,16 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId,
void updateDisplay(Volume volume, Boolean displayVolume);
- Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName) throws ResourceAllocationException;
+ Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName, Long vmSnapshotId) throws ResourceAllocationException;
/**
- * Checks if the target storage supports the disk offering.
+ * Checks if the storage pool supports the disk offering tags.
* This validation is consistent with the mechanism used to select a storage pool to deploy a volume when a virtual machine is deployed or when a data disk is allocated.
*
* The scenarios when this method returns true or false is presented in the following table.
*
*
- * | # | Disk offering tags | Storage tags | Does the storage support the disk offering? |
+ * # | Disk offering diskOfferingTags | Storage diskOfferingTags | Does the storage support the disk offering? |
*
*
*
@@ -169,7 +177,17 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId,
*
*
*/
- boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags);
+ boolean doesStoragePoolSupportDiskOffering(StoragePool destPool, DiskOffering diskOffering);
+
+ /**
+ * Checks if the storage pool supports the required disk offering tags
+ * destPool the storage pool to check the disk offering tags
+ * diskOfferingTags the tags that should be supported
+ * return whether the tags are supported in the storage pool
+ */
+ boolean doesStoragePoolSupportDiskOfferingTags(StoragePool destPool, String diskOfferingTags);
+
+ boolean validateConditionsToReplaceDiskOfferingOfVolume(Volume volume, DiskOffering newDiskOffering, StoragePool destPool);
Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge);
@@ -192,4 +210,6 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId,
Pair checkAndRepairVolume(CheckAndRepairVolumeCmd cmd) throws ResourceAllocationException;
Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo);
+
+ Long getCustomDiskOfferingIdForVolumeUpload(Account owner, DataCenter zone, boolean encryptEnabledOnly);
}
diff --git a/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java b/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java
index 67afd6aa4e24..d52e645ec799 100644
--- a/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java
+++ b/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java
@@ -85,7 +85,7 @@ public interface SnapshotApiService {
* the command that specifies the volume criteria
* @return list of snapshot policies
*/
- Pair, Integer> listPoliciesforVolume(ListSnapshotPoliciesCmd cmd);
+ Pair, Integer> listSnapshotPolicies(ListSnapshotPoliciesCmd cmd);
boolean deleteSnapshotPolicies(DeleteSnapshotPoliciesCmd cmd);
diff --git a/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java b/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java
index 22d5dfb9c1b8..13009a9808aa 100644
--- a/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java
+++ b/api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java
@@ -16,11 +16,12 @@
// under the License.
package com.cloud.storage.snapshot;
+import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.api.Displayable;
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
-public interface SnapshotPolicy extends Identity, InternalIdentity, Displayable {
+public interface SnapshotPolicy extends ControlledEntity, Identity, InternalIdentity, Displayable {
long getVolumeId();
diff --git a/api/src/main/java/com/cloud/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java
index 5b494c308c3c..6138f24c92b0 100644
--- a/api/src/main/java/com/cloud/template/TemplateApiService.java
+++ b/api/src/main/java/com/cloud/template/TemplateApiService.java
@@ -58,10 +58,23 @@ public interface TemplateApiService {
VirtualMachineTemplate prepareTemplate(long templateId, long zoneId, Long storageId);
+ /**
+ * Detach ISO from VM
+ * @param vmId id of the VM
+ * @param isoId id of the ISO (when passed). If it is not passed, it will get it from user_vm table
+ * @param extraParams forced, isVirtualRouter
+ * @return true when operation succeeds, false if not
+ */
+ boolean detachIso(long vmId, Long isoId, Boolean... extraParams);
- boolean detachIso(long vmId, boolean forced);
-
- boolean attachIso(long isoId, long vmId, boolean forced);
+ /**
+ * Attach ISO to a VM
+ * @param isoId id of the ISO to attach
+ * @param vmId id of the VM to attach the ISO to
+ * @param extraParams: forced, isVirtualRouter
+ * @return true when operation succeeds, false if not
+ */
+ boolean attachIso(long isoId, long vmId, Boolean... extraParams);
/**
* Deletes a template
diff --git a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java
index d8872d5fe724..b8c646048b97 100644
--- a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java
+++ b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java
@@ -145,10 +145,14 @@ public enum TemplateFilter {
boolean isDeployAsIs();
+ boolean isForCks();
+
Long getUserDataId();
UserData.UserDataOverridePolicy getUserDataOverridePolicy();
CPU.CPUArch getArch();
+ Long getExtensionId();
+
}
diff --git a/api/src/main/java/com/cloud/user/Account.java b/api/src/main/java/com/cloud/user/Account.java
index 6be4d0a48f6e..1cc5eae64a66 100644
--- a/api/src/main/java/com/cloud/user/Account.java
+++ b/api/src/main/java/com/cloud/user/Account.java
@@ -71,6 +71,7 @@ public static Type getFromValue(Integer type){
}
public static final long ACCOUNT_ID_SYSTEM = 1;
+ public static final long ACCOUNT_ID_ADMIN = 2;
public String getAccountName();
diff --git a/api/src/main/java/com/cloud/user/AccountService.java b/api/src/main/java/com/cloud/user/AccountService.java
index e2c3bed0c295..fc450e9179c5 100644
--- a/api/src/main/java/com/cloud/user/AccountService.java
+++ b/api/src/main/java/com/cloud/user/AccountService.java
@@ -21,12 +21,13 @@
import com.cloud.utils.Pair;
import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.RolePermissionEntity;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
+import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
+import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
+import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
-import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
-import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
-import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import com.cloud.dc.DataCenter;
import com.cloud.domain.Domain;
@@ -35,7 +36,16 @@
import com.cloud.offering.DiskOffering;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering;
+import org.apache.cloudstack.api.command.admin.user.DeleteUserKeysCmd;
+import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
+import org.apache.cloudstack.api.command.admin.user.ListUserKeyRulesCmd;
+import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd;
+import org.apache.cloudstack.api.command.admin.user.RegisterUserKeysCmd;
+import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
+import org.apache.cloudstack.api.response.ApiKeyPairResponse;
+import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
+import org.apache.cloudstack.backup.BackupOffering;
public interface AccountService {
@@ -58,7 +68,8 @@ UserAccount createUserAccount(String userName, String password, String firstName
User getSystemUser();
- User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID);
+ User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone,
+ String accountName, Long domainId, String userUUID, boolean isPasswordChangeRequired);
User createUser(String userName, String password, String firstName, String lastName, String email, String timeZone, String accountName, Long domainId, String userUUID,
User.Source source);
@@ -77,23 +88,31 @@ User createUser(String userName, String password, String firstName, String lastN
Account getActiveAccountById(long accountId);
+ Account getActiveAccountByUuid(String accountUuid);
+
Account getAccount(long accountId);
+ Account getAccountByUuid(String accountUuid);
+
User getActiveUser(long userId);
+ User getOneActiveUserForAccount(Account account);
+
User getUserIncludingRemoved(long userId);
boolean isRootAdmin(Long accountId);
boolean isDomainAdmin(Long accountId);
+ boolean isResourceDomainAdmin(Long accountId);
+
boolean isNormalUser(long accountId);
User getActiveUserByRegistrationToken(String registrationToken);
void markUserRegistered(long userId);
- public String[] createApiKeyAndSecretKey(RegisterCmd cmd);
+ ApiKeyPair createApiKeyAndSecretKey(RegisterUserKeysCmd cmd);
public String[] createApiKeyAndSecretKey(final long userId);
@@ -113,13 +132,19 @@ User createUser(String userName, String password, String firstName, String lastN
void checkAccess(Account account, VpcOffering vof, DataCenter zone) throws PermissionDeniedException;
+ void checkAccess(Account account, BackupOffering bof) throws PermissionDeniedException;
+
void checkAccess(User user, ControlledEntity entity);
void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException;
void validateAccountHasAccessToResource(Account account, AccessType accessType, Object resource);
- Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly);
+ void validateCallingUserHasAccessToDesiredUser(Long userId);
+
+ Long finalizeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly);
+
+ Long finalizeAccountId(Long accountId, String accountName, Long domainId, Long projectId);
/**
* returns the user account object for a given user id
@@ -128,9 +153,15 @@ User createUser(String userName, String password, String firstName, String lastN
*/
UserAccount getUserAccountById(Long userId);
- public Pair> getKeys(GetUserKeysCmd cmd);
+ Pair> getKeys(GetUserKeysCmd cmd);
+
+ ListResponse listKeys(ListUserKeysCmd cmd);
+
+ List listKeyRules(ListUserKeyRulesCmd cmd);
+
+ void deleteApiKey(DeleteUserKeysCmd cmd);
- public Pair> getKeys(Long userId);
+ void deleteApiKey(ApiKeyPair id);
/**
* Lists user two-factor authentication provider plugins
@@ -145,4 +176,13 @@ User createUser(String userName, String password, String firstName, String lastN
*/
UserTwoFactorAuthenticator getUserTwoFactorAuthenticationProvider(final Long domainId);
+ ApiKeyPair getLatestUserKeyPair(Long userId);
+
+ ApiKeyPair getKeyPairById(Long id);
+
+ ApiKeyPair getKeyPairByApiKey(String apiKey);
+
+ String getAccessingApiKey(BaseCmd cmd);
+
+ List getAllKeypairPermissions(String apiKey);
}
diff --git a/api/src/main/java/com/cloud/user/ApiKeyPairState.java b/api/src/main/java/com/cloud/user/ApiKeyPairState.java
new file mode 100644
index 000000000000..63405c62e320
--- /dev/null
+++ b/api/src/main/java/com/cloud/user/ApiKeyPairState.java
@@ -0,0 +1,21 @@
+// 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 com.cloud.user;
+
+public enum ApiKeyPairState {
+ ENABLED, REMOVED, EXPIRED
+}
diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java
index 2f4ad1347be5..9c493fb383c9 100644
--- a/api/src/main/java/com/cloud/user/ResourceLimitService.java
+++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java
@@ -30,11 +30,12 @@
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.template.VirtualMachineTemplate;
+import org.apache.cloudstack.resourcelimit.Reserver;
public interface ResourceLimitService {
static final ConfigKey MaxAccountSecondaryStorage = new ConfigKey<>("Account Defaults", Long.class, "max.account.secondary.storage", "400",
- "The default maximum secondary storage space (in GiB) that can be used for an account", false);
+ "The default maximum secondary storage space (in GiB) that can be used for an Account", false);
static final ConfigKey MaxProjectSecondaryStorage = new ConfigKey<>("Project Defaults", Long.class, "max.project.secondary.storage", "400",
"The default maximum secondary storage space (in GiB) that can be used for a project", false);
static final ConfigKey ResourceCountCheckInterval = new ConfigKey<>("Advanced", Long.class, "resourcecount.check.interval", "300",
@@ -50,8 +51,14 @@ public interface ResourceLimitService {
"The default maximum number of projects that can be created for an account",false);
static final ConfigKey DefaultMaxDomainProjects = new ConfigKey<>("Domain Defaults",Long.class,"max.domain.projects","50",
"The default maximum number of projects that can be created for a domain",false);
-
- static final List HostTagsSupportingTypes = List.of(ResourceType.user_vm, ResourceType.cpu, ResourceType.memory);
+ static final ConfigKey DefaultMaxAccountGpus = new ConfigKey<>("Account Defaults",Long.class,"max.account.gpus","20",
+ "The default maximum number of GPU devices that can be used for an account", false);
+ static final ConfigKey DefaultMaxDomainGpus = new ConfigKey<>("Domain Defaults",Long.class,"max.domain.gpus","20",
+ "The default maximum number of GPU devices that can be used for a domain", false);
+ static final ConfigKey DefaultMaxProjectGpus = new ConfigKey<>("Project Defaults",Long.class,"max.project.gpus","20",
+ "The default maximum number of GPU devices that can be used for a project", false);
+
+ static final List HostTagsSupportingTypes = List.of(ResourceType.user_vm, ResourceType.cpu, ResourceType.memory, ResourceType.gpu);
static final List StorageTagsSupportingTypes = List.of(ResourceType.volume, ResourceType.primary_storage);
/**
@@ -185,6 +192,7 @@ public interface ResourceLimitService {
*/
public void checkResourceLimit(Account account, ResourceCount.ResourceType type, long... count) throws ResourceAllocationException;
public void checkResourceLimitWithTag(Account account, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException;
+ public void checkResourceLimitWithTag(Account account, Long domainId, boolean considerSystemAccount, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException;
/**
* Gets the count of resources for a resource type and account
@@ -245,12 +253,12 @@ public interface ResourceLimitService {
List getResourceLimitStorageTags(DiskOffering diskOffering);
void updateTaggedResourceLimitsAndCountsForAccounts(List responses, String tag);
void updateTaggedResourceLimitsAndCountsForDomains(List responses, String tag);
- void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException;
-
+ void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException;
+ List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering);
void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize,
- DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException;
+ DiskOffering currentOffering, DiskOffering newOffering, List reservations) throws ResourceAllocationException;
- void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException;
+ void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException;
void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
@@ -267,21 +275,23 @@ void updateVolumeResourceCountForDiskOfferingChange(long accountId, Boolean disp
void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
- void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException;
+ void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException;
void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu,
- Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template) throws ResourceAllocationException;
+ Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException;
void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering,
- VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException;
+ VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException;
- void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException;
void incrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu);
void decrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu);
- void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException;
void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory);
void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory);
+ void incrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu);
+ void decrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu);
+
+ long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag);
}
diff --git a/api/src/main/java/com/cloud/user/User.java b/api/src/main/java/com/cloud/user/User.java
index 041b39ad2729..da7245a47980 100644
--- a/api/src/main/java/com/cloud/user/User.java
+++ b/api/src/main/java/com/cloud/user/User.java
@@ -65,14 +65,6 @@ public enum Source {
public void setState(Account.State state);
- public String getApiKey();
-
- public void setApiKey(String apiKey);
-
- public String getSecretKey();
-
- public void setSecretKey(String secretKey);
-
public String getTimezone();
public void setTimezone(String timezone);
diff --git a/api/src/main/java/com/cloud/user/UserAccount.java b/api/src/main/java/com/cloud/user/UserAccount.java
index e6b07fb371eb..5736244e3259 100644
--- a/api/src/main/java/com/cloud/user/UserAccount.java
+++ b/api/src/main/java/com/cloud/user/UserAccount.java
@@ -39,10 +39,6 @@ public interface UserAccount extends InternalIdentity {
String getState();
- String getApiKey();
-
- String getSecretKey();
-
Date getCreated();
Date getRemoved();
diff --git a/api/src/main/java/com/cloud/user/UserData.java b/api/src/main/java/com/cloud/user/UserData.java
index fa0c50473c0d..13a3c74f3679 100644
--- a/api/src/main/java/com/cloud/user/UserData.java
+++ b/api/src/main/java/com/cloud/user/UserData.java
@@ -29,4 +29,5 @@ public enum UserDataOverridePolicy {
String getUserData();
String getParams();
+ boolean isForCks();
}
diff --git a/api/src/main/java/com/cloud/vm/DiskProfile.java b/api/src/main/java/com/cloud/vm/DiskProfile.java
index 971ebde496e4..766573d4d40b 100644
--- a/api/src/main/java/com/cloud/vm/DiskProfile.java
+++ b/api/src/main/java/com/cloud/vm/DiskProfile.java
@@ -82,7 +82,7 @@ public DiskProfile(Volume vol, DiskOffering offering, HypervisorType hyperType)
null);
this.hyperType = hyperType;
this.provisioningType = offering.getProvisioningType();
- this.requiresEncryption = offering.getEncrypt() || vol.getPassphraseId() != null;
+ this.requiresEncryption = offering.getEncrypt() || vol.getPassphraseId() != null || vol.getKmsKeyId() != null;
}
public DiskProfile(DiskProfile dp) {
diff --git a/api/src/main/java/com/cloud/vm/Nic.java b/api/src/main/java/com/cloud/vm/Nic.java
index afc44b8d39fa..3722e5769c92 100644
--- a/api/src/main/java/com/cloud/vm/Nic.java
+++ b/api/src/main/java/com/cloud/vm/Nic.java
@@ -33,6 +33,11 @@
* Nic represents one nic on the VM.
*/
public interface Nic extends Identity, InternalIdentity {
+
+ interface Topics {
+ String NIC_LIFECYCLE = "nic.lifecycle";
+ }
+
enum Event {
ReservationRequested, ReleaseRequested, CancelRequested, OperationCompleted, OperationFailed,
}
@@ -162,4 +167,6 @@ public enum ReservationStrategy {
String getIPv6Address();
Integer getMtu();
+
+ boolean isEnabled();
}
diff --git a/api/src/main/java/com/cloud/vm/NicProfile.java b/api/src/main/java/com/cloud/vm/NicProfile.java
index a0c80ceb1bfb..d3ed7b4b87d2 100644
--- a/api/src/main/java/com/cloud/vm/NicProfile.java
+++ b/api/src/main/java/com/cloud/vm/NicProfile.java
@@ -52,6 +52,7 @@ public class NicProfile implements InternalIdentity, Serializable {
boolean defaultNic;
Integer networkRate;
boolean isSecurityGroupEnabled;
+ boolean enabled;
Integer orderIndex;
@@ -87,6 +88,7 @@ public NicProfile(Nic nic, Network network, URI broadcastUri, URI isolationUri,
broadcastType = network.getBroadcastDomainType();
trafficType = network.getTrafficType();
format = nic.getAddressFormat();
+ enabled = nic.isEnabled();
iPv4Address = nic.getIPv4Address();
iPv4Netmask = nic.getIPv4Netmask();
@@ -414,6 +416,14 @@ public void setIpv4AllocationRaceCheck(boolean ipv4AllocationRaceCheck) {
this.ipv4AllocationRaceCheck = ipv4AllocationRaceCheck;
}
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
//
// OTHER METHODS
//
diff --git a/api/src/main/java/com/cloud/vm/NicSecondaryIp.java b/api/src/main/java/com/cloud/vm/NicSecondaryIp.java
index 2856e0aea756..d25627c1782d 100644
--- a/api/src/main/java/com/cloud/vm/NicSecondaryIp.java
+++ b/api/src/main/java/com/cloud/vm/NicSecondaryIp.java
@@ -38,6 +38,8 @@ public interface NicSecondaryIp extends ControlledEntity, Identity, InternalIden
String getIp6Address();
+ String getDescription();
+
long getNetworkId();
long getVmId();
diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java
index 72b18b70e186..ffa00734d575 100644
--- a/api/src/main/java/com/cloud/vm/UserVmService.java
+++ b/api/src/main/java/com/cloud/vm/UserVmService.java
@@ -16,14 +16,18 @@
// under the License.
package com.cloud.vm;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.Volume;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import com.cloud.deploy.DeploymentPlan;
import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
+import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
@@ -36,6 +40,7 @@
import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
+import org.apache.cloudstack.api.command.user.vm.UpdateVmNicCmd;
import org.apache.cloudstack.api.command.user.vm.UpdateVmNicIpCmd;
import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd;
@@ -60,6 +65,7 @@
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
+import com.cloud.utils.Pair;
import com.cloud.utils.exception.ExecutionException;
public interface UserVmService {
@@ -111,7 +117,7 @@ UserVm startVirtualMachine(StartVMCmd cmd) throws StorageUnavailableException, E
UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException;
- void startVirtualMachine(UserVm vm) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException;
+ void startVirtualMachine(UserVm vm, DeploymentPlan plan) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException;
void startVirtualMachineForHA(VirtualMachine vm, Map params,
DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException,
@@ -147,6 +153,8 @@ void startVirtualMachineForHA(VirtualMachine vm, Map securityGroupIdList,
- Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod,
+ Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List