Skip to content

Commit

Permalink
add systemd update button
Browse files Browse the repository at this point in the history
  • Loading branch information
satkunas committed Dec 9, 2022
1 parent b5c93bb commit 4449474
Show file tree
Hide file tree
Showing 7 changed files with 429 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const props = {
},
acl: {
type: String,
default: 'SERVICES_READ'
default: 'SYSTEM_READ'
}
}
Expand Down
173 changes: 173 additions & 0 deletions html/pfappserver/root/src/components/new/BaseButtonSystemdUpdate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<template>
<b-dropdown ref="buttonRef"
:disabled="isDisabled"
:size="size"
:text="service"
no-flip
variant="outline-primary"
v-b-tooltip.hover.top.d300 :title="tooltip"
v-bind="$attrs"
>
<template v-slot:button-content>
<div class="d-inline px-1">
<icon v-if="!servers.length"
name="circle-notch" spin class="fa-overlap mr-1" />
<icon v-else-if="!Object.entries(cluster).length"
v-for="server of servers" :key="`icon-${server}`"
name="circle" class="text-secondary fa-overlap mr-1" />
<template v-else
v-for="(service, server) in cluster">
<icon v-if="service.status === 'loading'" :key="`icon-${server}`"
name="circle-notch" spin class="text-primary fa-overlap mr-1" />
<icon v-else-if="service.status === 'error'" :key="`icon-${server}`"
name="exclamation-triangle" class="text-danger fa-overlap mr-1" />
<icon v-else-if="service.status === 'updating'" :key="`icon-${server}`"
class="fa-overlap mr-1">
<icon name="circle" class="text-white" />
<icon v-if="service.status === 'updating'"
name="sync" class="text-primary" scale="0.5" />
</icon>
<icon v-else :key="`icon-${server}`"
name="circle" class="text-success fa-overlap mr-1" />
</template>
systemd
</div>
</template>

<b-dropdown-group v-if="isAllowed && isCluster"
:header="$i18n.t('CLUSTER')">
<b-dropdown-item @click="doUpdateAll" @click.stop="onClick" :disabled="isLoading"><icon name="sync" class="mr-1" /> {{ $t('Update All Sequentially') }}</b-dropdown-item>
</b-dropdown-group>

<template v-for="server of servers">
<b-dropdown-divider v-if="isCluster" :key="`divider-${server}`" />
<b-dropdown-group :key="`group-${server}`">
<template v-slot:header>
{{ server }}
</template>
<b-dropdown-form style="width: 400px;">
<base-systemd-update :id="service" :server="server" v-bind="{ acl }" />
</b-dropdown-form>
</b-dropdown-group>
</template>
</b-dropdown>
</template>
<script>
import BaseSystemdUpdate from './BaseSystemdUpdate'
const components = {
BaseSystemdUpdate
}
import { computed, nextTick, ref, toRefs } from '@vue/composition-api'
import acl from '@/utils/acl'
import i18n from '@/utils/locale'
import { localeStrings } from '@/globals/pfLocales'
const props = {
service: {
type: String,
default: 'pf'
},
size: {
type: String,
default: 'md',
validator: value => ['sm', 'md', 'lg'].includes(value)
},
disabled: {
type: Boolean
},
acl: {
type: String,
default: 'SERVICES_UPDATE'
}
}
const setup = (props, context) => {
const buttonRef = ref(null)
const onClick = event => {
event.stopPropagation()
nextTick(() => {
buttonRef.value.show() // keep open on click
})
}
const {
service,
disabled,
acl: _acl
} = toRefs(props)
const { root: { $store } = {}, emit } = context
const isAllowed = computed(() => {
if (_acl.value) {
const [ verb, ...nouns ] = Array.prototype.slice.call(_acl.value.toLowerCase().split('_')).reverse()
const noun = nouns.reverse().join('_')
return verb && nouns.length > 0 && acl.$can(verb, noun)
}
return true
})
const cluster = computed(() => {
const { [service.value]: { servers = {} } = {} } = $store.getters['cluster/systemdByServer']
return servers
})
const servers = computed(() => $store.getters['cluster/servers'])
const isCluster = computed(() => $store.getters['cluster/isCluster'])
const isLoading = computed(() => $store.getters['cluster/isLoading'])
const isDisabled = computed(() => disabled.value || !isAllowed.value || !servers.value.length)
const tooltip = computed(() => {
switch (true) {
case !isAllowed.value:
return i18n.t('No permission, admin role {acl} required.', { acl: _acl.value })
//break
default:
return undefined
}
})
const doUpdate = server => $store.dispatch('cluster/updateSystemd', { server, id: service.value }).then(() => {
$store.dispatch('notification/info', { url: server, message: i18n.t(localeStrings.SYSTEMD_UPDATED_SUCCESS, { service: `<code>${service.value}</code>` }) })
emit('update', { server, id: service.value })
}).catch(() => {
$store.dispatch('notification/danger', { url: server, message: i18n.t(localeStrings.SYSTEMD_UPDATED_ERROR, { service: `<code>${service.value}</code>` }) })
})
const doUpdateAll = () => $store.dispatch('cluster/updateSystemdCluster', service.value).then(() => {
$store.dispatch('notification/info', { url: 'CLUSTER', message: i18n.t(localeStrings.SYSTEMD_UPDATED_SUCCESS, { service: `<code>${service.value}</code>` }) })
emit('update', { id: service.value })
}).catch(() => {
$store.dispatch('notification/danger', { url: 'CLUSTER', message: i18n.t(localeStrings.SYSTEMD_UPDATED_ERROR, { service: `<code>${service.value}</code>` }) })
})
return {
buttonRef,
onClick,
isAllowed,
isCluster,
isDisabled,
isLoading,
cluster,
servers,
tooltip,
doUpdate,
doUpdateAll,
}
}
// @vue/component
export default {
name: 'base-button-systemd-update',
inheritAttrs: false,
components,
props,
setup
}
</script>
96 changes: 96 additions & 0 deletions html/pfappserver/root/src/components/new/BaseSystemdUpdate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<template>
<b-overlay :show="service.status && !['success', 'error'].includes(service.status)" variant="white">
<b-container fluid class="px-0">
<b-row class="row-nowrap mx-0" align-v="center">
<b-col cols="5" class="text-wrap" v-if="isAllowed">
<b-button
@click="doUpdate(server)" :disabled="isLoading" variant="link" size="sm" class="text-nowrap text-secondary mr-1">
<icon name="sync" class="mr-1" /> {{ $i18n.t('Update') }}
</b-button>
</b-col>
</b-row>
<b-row v-if="service.message"
class="mt-2 mx-0">
<b-col cols="auto" class="small text-danger text-wrap">
<icon name="info-circle" scale="1.5" class="mr-1" /> {{ service.message }}
</b-col>
</b-row>
</b-container>
<template v-slot:overlay v-if="service.status && service.status !== 'loading'">
<b-row class="justify-content-md-center">
<b-col cols="auto">
<b-media class="text-gray text-uppercase font-weight-bold">
<template v-slot:aside><icon name="circle-notch" spin scale="1.5" /></template>
<p v-if="service.status === 'updating'" class="mb-0">{{ $i18n.t('Updating') }}</p>
</b-media>
</b-col>
</b-row>
</template>
</b-overlay>
</template>
<script>
const props = {
id: {
type: String
},
server: {
type: String
},
acl: {
type: String,
default: 'SERVICES_UPDATE'
},
}
import { computed, toRefs } from '@vue/composition-api'
import acl from '@/utils/acl'
import i18n from '@/utils/locale'
import { localeStrings } from '@/globals/pfLocales'
const setup = (props, context) => {
const {
id,
server,
acl: _acl
} = toRefs(props)
const { emit, root: { $store } = {} } = context
const isAllowed = computed(() => {
if (_acl.value) {
const [ verb, ...nouns ] = Array.prototype.slice.call(_acl.value.toLowerCase().split('_')).reverse()
const noun = nouns.reverse().join('_')
return verb && nouns.length > 0 && acl.$can(verb, noun)
}
return true
})
const service = computed(() => $store.state.cluster.servers[server.value].systemd[id.value] || {})
const isLoading = computed(() => $store.getters['cluster/isLoading'])
const doUpdate = () => $store.dispatch('cluster/updateSystemd', { server: server.value, id: id.value }).then(() => {
$store.dispatch('notification/info', { url: server.value, message: i18n.t(localeStrings.SYSTEMD_UPDATED_SUCCESS, { service: `<code>${id.value}</code>` }) })
emit('update', { server, id: id.value })
}).catch(() => {
const message = i18n.t(localeStrings.SYSTEMD_UPDATED_ERROR, { services: `<code>${id.value}</code>` })
$store.dispatch('notification/danger', { url: server.value, message })
})
return {
service,
isAllowed,
isLoading,
doUpdate,
}
}
// @vue/component
export default {
name: 'base-systemd-update',
props,
setup
}
</script>
2 changes: 2 additions & 0 deletions html/pfappserver/root/src/components/new/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import BaseButtonSave from './BaseButtonSave'
import BaseButtonSaveSearch from './BaseButtonSaveSearch'
import BaseButtonService from './BaseButtonService'
import BaseButtonSystemService from './BaseButtonSystemService'
import BaseButtonSystemdUpdate from './BaseButtonSystemdUpdate'
import BaseButtonUpload from './BaseButtonUpload'
import BaseContainerLoading from './BaseContainerLoading'
import BaseCsvImport from './BaseCsvImport'
Expand Down Expand Up @@ -154,6 +155,7 @@ export {
BaseButtonSaveSearch,
BaseButtonService,
BaseButtonSystemService,
BaseButtonSystemdUpdate,
BaseButtonUpload,

// containers
Expand Down
3 changes: 3 additions & 0 deletions html/pfappserver/root/src/globals/pfLocales.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ export const localeStrings = {

SERVICES_STOPPED_SUCCESS: 'Stopped services {services}.', // i18n defer
SERVICES_STOPPED_ERROR: 'Failed to stop services {services}. See the server error logs for more information.', // i18n defer

SYSTEMD_UPDATED_SUCCESS: 'Updated systemd for {service}.', // i18n defer
SYSTEMD_UPDATED_ERROR: 'Failed to update systemd for {service}. See the server error logs for more information.', // i18n defer
}
Loading

0 comments on commit 4449474

Please sign in to comment.