1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-16 22:50:10 +03:00
This commit is contained in:
Tino Vazquez 2022-06-28 17:43:09 +02:00
commit 7fe69992a4
20 changed files with 276 additions and 41 deletions

View File

@ -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 "

View File

@ -65,15 +65,14 @@
<xs:element name="DOMAIN" type="xs:string"/>
<xs:element name="FUNCTION" type="xs:string"/>
<xs:element name="NUMA_NODE" type="xs:string"/>
<xs:element name="PROFILES" type="xs:string" minOccurs="0"/>
<xs:element name="SHORT_ADDRESS" type="xs:string"/>
<xs:element name="SLOT" type="xs:string"/>
<xs:element name="TYPE" type="xs:string"/>
<xs:element name="UUID" type="xs:string" minOccurs="0"/>
<xs:element name="VENDOR" type="xs:string"/>
<xs:element name="VENDOR_NAME" type="xs:string"/>
<xs:element name="VMID" type="xs:integer"/>
<xs:element name="UUID" type="xs:string"/>
<xs:element name="DEVICE_NAME" type="xs:string"/>
<xs:element name="PROFILES" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>

17
share/etc/guacd Normal file
View File

@ -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"

View File

@ -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
########

View File

@ -33,6 +33,8 @@ actions:
disable: true
delete: true
edit_labels: true
lock: true
unlock: true
# Filters - List of criteria to filter the resources

View File

@ -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 (
<>
<p>
<Translate word={T.Apps} />
{`: ${names.join(', ')}`}
</p>
<p>
<Translate word={T.DoYouWantProceed} />
</p>
</>
<Typography variant="inherit" component="span" display="block">
<Translate word={T.Apps} />
{`: ${names.join(', ')}`}
</Typography>
)
}
ListAppNames.propTypes = {
rows: PropTypes.arrayOf(
PropTypes.shape({
original: PropTypes.shape({
NAME: PropTypes.string,
}),
})
),
}
const SubHeader = (rows) => <ListAppNames rows={rows} />
const MessageToConfirmAction = (rows) => (
<>
<ListAppNames rows={rows} />
<Translate word={T.DoYouWantProceed} />
</>
)
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]

View File

@ -374,7 +374,6 @@ const Actions = () => {
form: MigrateForm,
dialogProps: {
title: T.Deploy,
subheader: SubHeader,
dataCy: `modal-${VM_ACTIONS.DEPLOY}`,
},
onSubmit: (rows) => async (formData) => {

View File

@ -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,
})
}
}

View File

@ -49,6 +49,8 @@ const Content = ({
renderContent: RenderContent,
hidden,
addBorder = false,
setTab,
logTabId,
}) => (
<TabContent
key={`tab-${id ?? name}`}
@ -59,7 +61,7 @@ const Content = ({
<Fade in timeout={400}>
<TabContent sx={{ p: '1em .5em' }}>
{typeof RenderContent === 'function' ? (
<RenderContent />
<RenderContent setTab={setTab} logTabId={logTabId} />
) : (
RenderContent
)}
@ -129,6 +131,12 @@ const Tabs = ({
[tabSelected]
)
const logTabId = tabs
.map(function (tabProps) {
return tabProps.name
})
.indexOf('log')
return (
<Stack height={1} overflow="auto">
<Fade in timeout={300}>
@ -139,7 +147,9 @@ const Tabs = ({
) : (
<Content
addBorder={addBorder}
setTab={setTab}
{...tabs.find(({ value }, idx) => (value ?? idx) === tabSelected)}
logTabId={logTabId}
/>
)}
</Stack>
@ -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

View File

@ -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,

View File

@ -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',

View File

@ -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)
}}
/>
<SubmitButton
@ -137,7 +138,11 @@ const Hosts = memo(({ id }) => {
)
})
Hosts.propTypes = { id: PropTypes.string.isRequired }
Hosts.propTypes = {
id: PropTypes.string.isRequired,
setTab: PropTypes.func,
logTabId: PropTypes.number,
}
Hosts.displayName = 'Hosts'
export default Hosts

View File

@ -63,7 +63,9 @@ const DialogInfo = ({ id }) => {
name: 'hosts',
label: T.Hosts,
icon: HostIcon,
renderContent: () => <HostsTab id={id} />,
renderContent: ({ setTab, logTabId }) => (
<HostsTab id={id} setTab={setTab} logTabId={logTabId} />
),
},
{
name: 'log',

View File

@ -123,7 +123,7 @@ const Settings = () => {
<Stack height={1} gap="0.5rem" p="0.5rem" overflow="auto">
{labels.length === 0 && (
<Typography variant="subtitle2">
<Translate word={T.NoLabels} />
<Translate word={T.NoLabelsOnList} />
</Typography>
)}
{result?.map((label) => (

View File

@ -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

View File

@ -315,7 +315,7 @@ export const getAvailableInfoTabs = (infoTabs = {}, getTabComponent, id) =>
return (
TabContent && {
name: camelName,
label: camelName,
id: tabName,
renderContent: () => <TabContent tabProps={tabProps} id={id} />,
}

View File

@ -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))

View File

@ -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
}

View File

@ -158,11 +158,11 @@ module.exports = {
from: resource,
default: 0,
},
userId: {
user: {
from: postBody,
default: -1,
},
groupId: {
group: {
from: postBody,
default: -1,
},

View File

@ -38,7 +38,7 @@ string HookStateVirtualNetwork::format_message(VirtualNetwork * vn)
oss << "<HOOK_MESSAGE>"
<< "<HOOK_TYPE>STATE</HOOK_TYPE>"
<< "<HOOK_OBJECT>VNET</HOOK_OBJECT>"
<< "<HOOK_OBJECT>NET</HOOK_OBJECT>"
<< "<STATE>" << VirtualNetwork::state_to_str(vn->get_state()) << "</STATE>"
<< "<RESOURCE_ID>" << vn->get_oid() << "</RESOURCE_ID>"
<< vn->to_xml(vn_xml)