diff --git a/install.sh b/install.sh
index 9cb875469c..97318df647 100755
--- a/install.sh
+++ b/install.sh
@@ -2269,6 +2269,7 @@ ONEVMDUMP_LIB_RESTORERS_FILES="src/onevmdump/lib/restorers/base.rb"
ETC_FILES="share/etc/oned.conf \
share/etc/defaultrc \
+ share/etc/guacd \
src/tm_mad/tmrc \
src/scheduler/etc/sched.conf \
src/monitor/etc/monitord.conf "
diff --git a/share/doc/xsd/host.xsd b/share/doc/xsd/host.xsd
index 2b68aed002..cc25cdf7f2 100644
--- a/share/doc/xsd/host.xsd
+++ b/share/doc/xsd/host.xsd
@@ -65,15 +65,14 @@
+
+
-
-
-
diff --git a/share/etc/guacd b/share/etc/guacd
new file mode 100644
index 0000000000..a7c2b247fe
--- /dev/null
+++ b/share/etc/guacd
@@ -0,0 +1,17 @@
+# -------------------------------------------------------------------------- #
+# Copyright 2002-2022, OpenNebula Project, OpenNebula Systems #
+# #
+# Licensed 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. #
+#--------------------------------------------------------------------------- #
+
+OPTS="-b 0.0.0.0"
\ No newline at end of file
diff --git a/share/linters/.rubocop.yml b/share/linters/.rubocop.yml
index 6d3d7ce91c..4f3e1c9a4b 100644
--- a/share/linters/.rubocop.yml
+++ b/share/linters/.rubocop.yml
@@ -559,6 +559,12 @@ Layout/MultilineOperationIndentation:
Layout/LineEndStringConcatenationIndentation:
Enabled: false
+Layout/LineContinuationSpacing:
+ Enabled: false
+
+Layout/LineContinuationLeadingSpace:
+ Enabled: false
+
#######
# STYLE
#######
@@ -914,6 +920,9 @@ Lint/UnmodifiedReduceAccumulator:
Lint/AmbiguousOperatorPrecedence:
Enabled: false
+Lint/NonAtomicFileOperation:
+ Enabled: false
+
#########
# METRICS
########
diff --git a/src/fireedge/etc/sunstone/admin/marketplace-app-tab.yaml b/src/fireedge/etc/sunstone/admin/marketplace-app-tab.yaml
index 38ff64cfea..19a7c19090 100644
--- a/src/fireedge/etc/sunstone/admin/marketplace-app-tab.yaml
+++ b/src/fireedge/etc/sunstone/admin/marketplace-app-tab.yaml
@@ -33,6 +33,8 @@ actions:
disable: true
delete: true
edit_labels: true
+ lock: true
+ unlock: true
# Filters - List of criteria to filter the resources
diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js
index 726cbd2b87..61d2573d2d 100644
--- a/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js
+++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js
@@ -19,7 +19,12 @@ import {
AddCircledOutline,
CloudDownload,
DownloadCircledOutline,
+ Lock,
+ MoreVert,
+ Group,
+ Trash,
} from 'iconoir-react'
+import PropTypes from 'prop-types'
import { useViews } from 'client/features/Auth'
import { useGeneralApi } from 'client/features/General'
@@ -27,6 +32,12 @@ import { Translate } from 'client/components/HOC'
import {
useExportAppMutation,
useDownloadAppMutation,
+ useLockAppMutation,
+ useUnlockAppMutation,
+ useEnableAppMutation,
+ useDisableAppMutation,
+ useChangeAppOwnershipMutation,
+ useDeleteAppMutation,
} from 'client/features/OneApi/marketplaceApp'
import { ExportForm } from 'client/components/Forms/MarketplaceApp'
@@ -37,22 +48,37 @@ import {
import { PATH } from 'client/apps/sunstone/routesOne'
import { T, RESOURCE_NAMES, MARKETPLACE_APP_ACTIONS } from 'client/constants'
+import { ChangeGroupForm, ChangeUserForm } from 'client/components/Forms/Vm'
+import { Typography } from '@mui/material'
-const MessageToConfirmAction = (rows) => {
+const ListAppNames = ({ rows = [] }) => {
const names = rows?.map?.(({ original }) => original?.NAME)
return (
- <>
-
-
- {`: ${names.join(', ')}`}
-
-
-
-
- >
+
+
+ {`: ${names.join(', ')}`}
+
)
}
+ListAppNames.propTypes = {
+ rows: PropTypes.arrayOf(
+ PropTypes.shape({
+ original: PropTypes.shape({
+ NAME: PropTypes.string,
+ }),
+ })
+ ),
+}
+
+const SubHeader = (rows) =>
+
+const MessageToConfirmAction = (rows) => (
+ <>
+
+
+ >
+)
MessageToConfirmAction.displayName = 'MessageToConfirmAction'
@@ -67,6 +93,12 @@ const Actions = () => {
const { enqueueSuccess } = useGeneralApi()
const [exportApp] = useExportAppMutation()
const [downloadApp] = useDownloadAppMutation()
+ const [lock] = useLockAppMutation()
+ const [unlock] = useUnlockAppMutation()
+ const [enable] = useEnableAppMutation()
+ const [disable] = useDisableAppMutation()
+ const [changeOwnership] = useChangeAppOwnershipMutation()
+ const [deleteApp] = useDeleteAppMutation()
const marketplaceAppActions = useMemo(
() =>
@@ -117,6 +149,142 @@ const Actions = () => {
urls.forEach((url) => window.open(url, '_blank'))
},
},
+ {
+ tooltip: T.Lock,
+ icon: Lock,
+ selected: true,
+ color: 'secondary',
+ dataCy: 'marketapp-lock',
+ options: [
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.LOCK,
+ name: T.Lock,
+ isConfirmDialog: true,
+ dialogProps: {
+ title: T.Lock,
+ children: MessageToConfirmAction,
+ dataCy: `modal-${MARKETPLACE_APP_ACTIONS.LOCK}`,
+ },
+ onSubmit: (rows) => async () => {
+ const ids = rows?.map?.(({ original }) => original?.ID)
+ await Promise.all(ids.map((id) => lock({ id })))
+ },
+ },
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.UNLOCK,
+ name: T.Unlock,
+ isConfirmDialog: true,
+ dialogProps: {
+ title: T.Unlock,
+ children: MessageToConfirmAction,
+ dataCy: `modal-${MARKETPLACE_APP_ACTIONS.UNLOCK}`,
+ },
+ onSubmit: (rows) => async () => {
+ const ids = rows?.map?.(({ original }) => original?.ID)
+ await Promise.all(ids.map((id) => unlock({ id })))
+ },
+ },
+ ],
+ },
+ {
+ tooltip: T.Enable,
+ icon: MoreVert,
+ selected: true,
+ color: 'secondary',
+ dataCy: 'marketapp-enable',
+ options: [
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.ENABLE,
+ name: T.Enable,
+ isConfirmDialog: true,
+ dialogProps: {
+ title: T.Enable,
+ children: MessageToConfirmAction,
+ dataCy: `modal-${MARKETPLACE_APP_ACTIONS.ENABLE}`,
+ },
+ onSubmit: (rows) => async () => {
+ const ids = rows?.map?.(({ original }) => original?.ID)
+ await Promise.all(ids.map((id) => enable(id)))
+ },
+ },
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.DISABLE,
+ name: T.Disable,
+ isConfirmDialog: true,
+ dialogProps: {
+ title: T.Disable,
+ children: MessageToConfirmAction,
+ dataCy: `modal-${MARKETPLACE_APP_ACTIONS.DISABLE}`,
+ },
+ onSubmit: (rows) => async () => {
+ const ids = rows?.map?.(({ original }) => original?.ID)
+ await Promise.all(ids.map((id) => disable(id)))
+ },
+ },
+ ],
+ },
+ {
+ tooltip: T.Ownership,
+ icon: Group,
+ selected: true,
+ color: 'secondary',
+ dataCy: 'vm-ownership',
+ options: [
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.CHANGE_OWNER,
+ name: T.ChangeOwner,
+ dialogProps: {
+ title: T.ChangeOwner,
+ subheader: SubHeader,
+ dataCy: `modal-${MARKETPLACE_APP_ACTIONS.CHANGE_OWNER}`,
+ },
+ form: ChangeUserForm,
+ onSubmit: (rows) => async (newOwnership) => {
+ const ids = rows?.map?.(({ original }) => original?.ID)
+ await Promise.all(
+ ids.map((id) => changeOwnership({ id, ...newOwnership }))
+ )
+ },
+ },
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.CHANGE_GROUP,
+ name: T.ChangeGroup,
+ dialogProps: {
+ title: T.ChangeGroup,
+ subheader: SubHeader,
+ dataCy: `modal-${MARKETPLACE_APP_ACTIONS.CHANGE_GROUP}`,
+ },
+ form: ChangeGroupForm,
+ onSubmit: (rows) => async (newOwnership) => {
+ const ids = rows?.map?.(({ original }) => original?.ID)
+ await Promise.all(
+ ids.map((id) => changeOwnership({ id, ...newOwnership }))
+ )
+ },
+ },
+ ],
+ },
+ {
+ accessor: MARKETPLACE_APP_ACTIONS.DELETE,
+ tooltip: T.Delete,
+ icon: Trash,
+ color: 'error',
+ selected: { min: 1 },
+ options: [
+ {
+ isConfirmDialog: true,
+ dialogProps: {
+ title: T.Delete,
+ dataCy: `modal-${MARKETPLACE_APP_ACTIONS.DELETE}`,
+ children: MessageToConfirmAction,
+ },
+ onSubmit: (rows) => async () => {
+ const ids = rows?.map?.(({ original }) => original?.ID)
+ await Promise.all(ids.map((id) => deleteApp({ id })))
+ },
+ },
+ ],
+ },
],
}),
[view]
diff --git a/src/fireedge/src/client/components/Tables/Vms/actions.js b/src/fireedge/src/client/components/Tables/Vms/actions.js
index ece85dc1de..b33284ed88 100644
--- a/src/fireedge/src/client/components/Tables/Vms/actions.js
+++ b/src/fireedge/src/client/components/Tables/Vms/actions.js
@@ -374,7 +374,6 @@ const Actions = () => {
form: MigrateForm,
dialogProps: {
title: T.Deploy,
- subheader: SubHeader,
dataCy: `modal-${VM_ACTIONS.DEPLOY}`,
},
onSubmit: (rows) => async (formData) => {
diff --git a/src/fireedge/src/client/components/Tabs/Vm/Configuration.js b/src/fireedge/src/client/components/Tabs/Vm/Configuration.js
index a3ccd220e2..0674419441 100644
--- a/src/fireedge/src/client/components/Tabs/Vm/Configuration.js
+++ b/src/fireedge/src/client/components/Tabs/Vm/Configuration.js
@@ -68,7 +68,13 @@ const VmConfigurationTab = ({ tabProps: { actions } = {}, id }) => {
if (isSupported && hasValue) {
const name = idx ? `${idx}.${key}` : key
- sectionAttributes.push({ name, value, dataCy: name })
+ sectionAttributes.push({
+ name,
+ value,
+ dataCy: name,
+ canCopy: true,
+ showActionsOnHover: true,
+ })
}
}
diff --git a/src/fireedge/src/client/components/Tabs/index.js b/src/fireedge/src/client/components/Tabs/index.js
index 6bd4302751..6e232e6893 100644
--- a/src/fireedge/src/client/components/Tabs/index.js
+++ b/src/fireedge/src/client/components/Tabs/index.js
@@ -49,6 +49,8 @@ const Content = ({
renderContent: RenderContent,
hidden,
addBorder = false,
+ setTab,
+ logTabId,
}) => (
{typeof RenderContent === 'function' ? (
-
+
) : (
RenderContent
)}
@@ -129,6 +131,12 @@ const Tabs = ({
[tabSelected]
)
+ const logTabId = tabs
+ .map(function (tabProps) {
+ return tabProps.name
+ })
+ .indexOf('log')
+
return (
@@ -139,7 +147,9 @@ const Tabs = ({
) : (
(value ?? idx) === tabSelected)}
+ logTabId={logTabId}
/>
)}
@@ -162,6 +172,8 @@ Content.propTypes = {
renderContent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
hidden: PropTypes.bool,
addBorder: PropTypes.bool,
+ setTab: PropTypes.func,
+ logTabId: PropTypes.number,
}
export default Tabs
diff --git a/src/fireedge/src/client/constants/marketplaceApp.js b/src/fireedge/src/client/constants/marketplaceApp.js
index aa8d8e7ee7..d78e312d93 100644
--- a/src/fireedge/src/client/constants/marketplaceApp.js
+++ b/src/fireedge/src/client/constants/marketplaceApp.js
@@ -56,6 +56,8 @@ export const MARKETPLACE_APP_ACTIONS = {
DISABLE: 'disable',
DELETE: 'delete',
EDIT_LABELS: 'edit_labels',
+ LOCK: 'lock',
+ UNLOCK: 'unlock',
// INFORMATION
RENAME: ACTIONS.RENAME,
diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js
index 06485b55c8..7fd13ca96a 100644
--- a/src/fireedge/src/client/constants/translates.js
+++ b/src/fireedge/src/client/constants/translates.js
@@ -305,6 +305,7 @@ module.exports = {
LabelAlreadyExists: 'Label already exists',
PressToCreateLabel: 'Press enter to create a new label',
SavesInTheUserTemplate: "Saved in the User's template",
+ NoLabelsOnList: 'You have not defined any labels, list is empty',
/* sections - system */
User: 'User',
diff --git a/src/fireedge/src/client/containers/Provisions/DialogInfo/hosts.js b/src/fireedge/src/client/containers/Provisions/DialogInfo/hosts.js
index edcb983406..c73fff4696 100644
--- a/src/fireedge/src/client/containers/Provisions/DialogInfo/hosts.js
+++ b/src/fireedge/src/client/containers/Provisions/DialogInfo/hosts.js
@@ -38,7 +38,7 @@ import { SubmitButton } from 'client/components/FormControl'
import { Translate } from 'client/components/HOC'
import { T } from 'client/constants'
-const Hosts = memo(({ id }) => {
+const Hosts = memo(({ id, setTab, logTabId }) => {
const [amount, setAmount] = useState(() => 1)
const { enqueueInfo } = useGeneralApi()
@@ -113,6 +113,7 @@ const Hosts = memo(({ id }) => {
onClick={async () => {
configureHost({ provision: id, id: host.ID })
enqueueInfo(`Configuring host - ID: ${host.ID}`)
+ setTab(logTabId)
}}
/>
{
)
})
-Hosts.propTypes = { id: PropTypes.string.isRequired }
+Hosts.propTypes = {
+ id: PropTypes.string.isRequired,
+ setTab: PropTypes.func,
+ logTabId: PropTypes.number,
+}
Hosts.displayName = 'Hosts'
export default Hosts
diff --git a/src/fireedge/src/client/containers/Provisions/DialogInfo/index.js b/src/fireedge/src/client/containers/Provisions/DialogInfo/index.js
index 3d187478ee..e79dc8bcaa 100644
--- a/src/fireedge/src/client/containers/Provisions/DialogInfo/index.js
+++ b/src/fireedge/src/client/containers/Provisions/DialogInfo/index.js
@@ -63,7 +63,9 @@ const DialogInfo = ({ id }) => {
name: 'hosts',
label: T.Hosts,
icon: HostIcon,
- renderContent: () => ,
+ renderContent: ({ setTab, logTabId }) => (
+
+ ),
},
{
name: 'log',
diff --git a/src/fireedge/src/client/containers/Settings/LabelsSection/index.js b/src/fireedge/src/client/containers/Settings/LabelsSection/index.js
index 279daab505..3e33b550cb 100644
--- a/src/fireedge/src/client/containers/Settings/LabelsSection/index.js
+++ b/src/fireedge/src/client/containers/Settings/LabelsSection/index.js
@@ -123,7 +123,7 @@ const Settings = () => {
{labels.length === 0 && (
-
+
)}
{result?.map((label) => (
diff --git a/src/fireedge/src/client/features/OneApi/marketplaceApp.js b/src/fireedge/src/client/features/OneApi/marketplaceApp.js
index e6dc6341ad..6b076ff046 100644
--- a/src/fireedge/src/client/features/OneApi/marketplaceApp.js
+++ b/src/fireedge/src/client/features/OneApi/marketplaceApp.js
@@ -484,6 +484,23 @@ const marketAppApi = oneApi.injectEndpoints({
},
invalidatesTags: [APP_POOL],
}),
+ deleteApp: builder.mutation({
+ /**
+ * Delete Marketplaceapp.
+ *
+ * @param {object} params - Request parameters
+ * @param {string} params.id - Marketplaceapp ID
+ * @returns {number} Marketplace app id
+ * @throws Fails when response isn't code 200
+ */
+ query: (params) => {
+ const name = Actions.MARKETAPP_DELETE
+ const command = { name, ...Commands[name] }
+
+ return { params, command }
+ },
+ invalidatesTags: (_, __, { id }) => [{ type: APP, id }],
+ }),
downloadApp: builder.mutation({
/**
* Download a MarketPlaceApp.
@@ -559,6 +576,7 @@ export const {
useImportAppMutation,
useExportAppMutation,
useDownloadAppMutation,
+ useDeleteAppMutation,
} = marketAppApi
export default marketAppApi
diff --git a/src/fireedge/src/client/models/Helper.js b/src/fireedge/src/client/models/Helper.js
index 378a596575..97cde2a887 100644
--- a/src/fireedge/src/client/models/Helper.js
+++ b/src/fireedge/src/client/models/Helper.js
@@ -315,7 +315,7 @@ export const getAvailableInfoTabs = (infoTabs = {}, getTabComponent, id) =>
return (
TabContent && {
- name: camelName,
+ label: camelName,
id: tabName,
renderContent: () => ,
}
diff --git a/src/fireedge/src/server/routes/api/auth/utils.js b/src/fireedge/src/server/routes/api/auth/utils.js
index 5541d4419a..02167c3277 100644
--- a/src/fireedge/src/server/routes/api/auth/utils.js
+++ b/src/fireedge/src/server/routes/api/auth/utils.js
@@ -45,10 +45,6 @@ const {
const { ok, unauthorized, accepted, internalServerError } = httpCodes
-const appConfig = getFireedgeConfig()
-
-const namespace = appConfig.namespace || defaultNamespace
-
const { GET } = httpMethod
let user = ''
@@ -200,6 +196,7 @@ const setRes = (newRes = {}) => {
* Set dates.
*/
const setDates = () => {
+ const appConfig = getFireedgeConfig()
limitToken = remember
? appConfig.session_remember_expiration || defaultRememberSessionExpiration
: appConfig.session_expiration || defaultSessionExpiration
@@ -314,13 +311,7 @@ const genJWT = (token, informationUser) => {
* @returns {object} - user token
*/
const getCreatedTokenOpennebula = (username = '') => {
- if (
- global &&
- global.users &&
- username &&
- global.users[username] &&
- global.users[username].tokens
- ) {
+ if (username && global?.users?.[username]?.tokens) {
let acc = { token: '', time: 0 }
global.users[username].tokens.forEach((curr = {}, index = 0) => {
const currentTime = parseInt(curr.time, 10)
@@ -550,6 +541,8 @@ const getServerAdminAndWrapUser = (userData = {}) => {
const login = (userData) => {
let rtn = false
if (userData) {
+ const appConfig = getFireedgeConfig()
+ const namespace = appConfig.namespace || defaultNamespace
const findTextError = `[${namespace}.${ActionUsers.USER_INFO}]`
if (userData.indexOf && userData.indexOf(findTextError) >= 0) {
updaterResponse(httpResponse(unauthorized))
diff --git a/src/fireedge/src/server/routes/entrypoints/Api/middlawares.js b/src/fireedge/src/server/routes/entrypoints/Api/middlawares.js
index f065636dd1..9741308a07 100644
--- a/src/fireedge/src/server/routes/entrypoints/Api/middlawares.js
+++ b/src/fireedge/src/server/routes/entrypoints/Api/middlawares.js
@@ -14,6 +14,7 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
const { env } = require('process')
+const { DateTime } = require('luxon')
const { httpCodes, defaults } = require('server/utils/constants')
const { getFireedgeConfig } = require('server/utils/yml')
const { defaultWebpackMode, defaultEmptyFunction, defaultOpennebulaZones } =
@@ -33,16 +34,16 @@ let passOpennebula = ''
* @returns {boolean} user valid data
*/
const userValidation = (user = '', token = '') => {
+ const nowUnix = DateTime.local().toSeconds()
let rtn = false
if (
user &&
token &&
- global &&
- global.users &&
- global.users[user] &&
- global.users[user].tokens &&
- Array.isArray(global.users[user].tokens) &&
- global.users[user].tokens.some((x) => x && x.token === token)
+ Array.isArray(global?.users?.[user]?.tokens) &&
+ global?.users?.[user]?.tokens?.some?.(
+ ({ token: internalToken, time }) =>
+ time > nowUnix && internalToken === token
+ )
) {
rtn = true
}
diff --git a/src/fireedge/src/server/utils/constants/commands/marketapp.js b/src/fireedge/src/server/utils/constants/commands/marketapp.js
index 38c2bb385d..8378008663 100644
--- a/src/fireedge/src/server/utils/constants/commands/marketapp.js
+++ b/src/fireedge/src/server/utils/constants/commands/marketapp.js
@@ -158,11 +158,11 @@ module.exports = {
from: resource,
default: 0,
},
- userId: {
+ user: {
from: postBody,
default: -1,
},
- groupId: {
+ group: {
from: postBody,
default: -1,
},
diff --git a/src/hm/HookStateVirtualNetwork.cc b/src/hm/HookStateVirtualNetwork.cc
index d7d39b6db9..a11ea56380 100644
--- a/src/hm/HookStateVirtualNetwork.cc
+++ b/src/hm/HookStateVirtualNetwork.cc
@@ -38,7 +38,7 @@ string HookStateVirtualNetwork::format_message(VirtualNetwork * vn)
oss << ""
<< "STATE"
- << "VNET"
+ << "NET"
<< "" << VirtualNetwork::state_to_str(vn->get_state()) << ""
<< "" << vn->get_oid() << ""
<< vn->to_xml(vn_xml)