Skip to content

Commit

Permalink
fix(xapi/parseDateTime): handle date objects (#6701)
Browse files Browse the repository at this point in the history
Fixes zammad#12622 zammad#13106 zammad#13136 zammad#13162
  • Loading branch information
julien-f committed Mar 6, 2023
1 parent aec5ad4 commit 473d091
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 30 deletions.
40 changes: 40 additions & 0 deletions @xen-orchestra/xapi/_parseDateTime.test.js
@@ -0,0 +1,40 @@
'use strict'

const assert = require('node:assert/strict')
const test = require('test')

const { parseDateTime } = require('./')

test('parseDateTime()', function () {
for (const [input, output] of [
// number
[0, null],
[1678040309, 1678040309],

// string
['19700101T00:00:00Z', null],
['20230305T18:18:29Z', 1678040309],
['0', null],
['1675817747.', 1675817747],
['1647628715.91', 1647628715.91],

// date
[new Date(0), null],
[new Date(1678040309000), 1678040309],
]) {
assert.equal(parseDateTime(input), output)
}
})

test('failing parseDateTime()' , function(){
for (const [input, error] of [
// object
[{}, TypeError],
[undefined, TypeError],

// string
['XXX 03/10/2010', RangeError]
]) {
assert.throws(() => parseDateTime(input), error)
}
})
46 changes: 39 additions & 7 deletions @xen-orchestra/xapi/index.js
Expand Up @@ -18,15 +18,47 @@ exports.VDI_FORMAT_VHD = 'vhd'
exports.formatDateTime = utcFormat('%Y%m%dT%H:%M:%SZ')

const parseDateTimeHelper = utcParse('%Y%m%dT%H:%M:%SZ')
exports.parseDateTime = function (str, defaultValue) {
const date = parseDateTimeHelper(str)
if (date === null) {
if (arguments.length > 1) {
return defaultValue

/**
* Parses a date and time input and returns a Unix timestamp in seconds.
*
* @param {string|number|Date} input - The input to parse.
* @returns {number|null} A Unix timestamp in seconds, or null if the field is empty (as encoded by XAPI).
* @throws {TypeError} If the input is not a string, number or Date object.
*/
exports.parseDateTime = function parseDateTime(input) {
const type = typeof input

// If the value is a number, it is assumed to be a timestamp in seconds
if (type === 'number') {
return input || null
}

if (typeof input === 'string') {
let date

// Some dates like host.other_config.{agent_start_time,boot_time,rpm_patch_installation_time}
// are already timestamps
date = +input
if (!Number.isNaN(date)) {
return date || null
}
throw new RangeError(`unable to parse XAPI datetime ${JSON.stringify(str)}`)

// This is the case when the date has been retrieved via the JSON-RPC or JSON in XML-RPC APIs.
date = parseDateTimeHelper(input)
if (date === null) {
throw new RangeError(`unable to parse XAPI datetime ${JSON.stringify(input)}`)
}
input = date
}

// This is the case when the date has been retrieved using the XML-RPC API or parsed by the block above.
if (input instanceof Date) {
const msTimestamp = input.getTime()
return msTimestamp === 0 ? null : Math.floor(msTimestamp / 1e3)
}
return date.getTime()

throw new TypeError('unsupported input ' + input)
}

const hasProps = o => {
Expand Down
6 changes: 5 additions & 1 deletion @xen-orchestra/xapi/package.json
Expand Up @@ -18,7 +18,8 @@
"xen-api": "^1.2.6"
},
"scripts": {
"postversion": "npm publish --access public"
"postversion": "npm publish --access public",
"test": "node--test"
},
"dependencies": {
"@vates/decorate-with": "^2.0.0",
Expand All @@ -38,5 +39,8 @@
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"devDependencies": {
"test": "^3.3.0"
}
}
2 changes: 2 additions & 0 deletions CHANGELOG.unreleased.md
Expand Up @@ -13,6 +13,7 @@
- [Import VM] fix invalid parameters when importing a VM from VMware [Forum#6714](https://xcp-ng.org/forum/topic/6714/vmware-migration-tool-we-need-your-feedback/143) (PR [#6696](https://github.com/vatesfr/xen-orchestra/pull/6696))
- [Backup] Fix _A "socket" was not created for HTTP request before 300000ms_ error [Forum#59163](https://xcp-ng.org/forum/post/59163) [#6656](https://github.com/vatesfr/xen-orchestra/issues/6656)
- Fix display of dates (e.g. _13 Apr 55055_ instead of _01 Feb 2023_) [Forum#58965](https://xcp-ng.org/forum/post/58965) [Forum#59605](https://xcp-ng.org/forum/post/59605)

### Packages to release

Expand All @@ -30,6 +31,7 @@
<!--packages-start-->

- @xen-orchestra/xapi major
- xen-api patch
- xo-web patch
- xo-server minor
Expand Down
26 changes: 9 additions & 17 deletions packages/xo-server/src/xapi-object-to-xo.mjs
Expand Up @@ -2,11 +2,14 @@ import { isDefaultTemplate, parseDateTime } from '@xen-orchestra/xapi'

import * as sensitiveValues from './sensitive-values.mjs'
import ensureArray from './_ensureArray.mjs'
import { createLogger } from '@xen-orchestra/log'
import { extractIpFromVmNetworks } from './_extractIpFromVmNetworks.mjs'
import { extractProperty, forEach, isEmpty, mapFilter, parseXml } from './utils.mjs'
import { getVmDomainType, isHostRunning, isVmRunning } from './xapi/index.mjs'
import { useUpdateSystem } from './xapi/utils.mjs'

const { warn } = createLogger('xo:server:xapi-objects-to-xo')

// ===================================================================

const ALLOCATION_BY_TYPE = {
Expand Down Expand Up @@ -52,28 +55,17 @@ function link(obj, prop, idField = '$id') {
return dynamicValue[idField]
}

// Parse a string date time to a Unix timestamp (in seconds).
//
// If the value is a number or can be converted as one, it is assumed
// to already be a timestamp and returned.
//
// If there are no data or if the timestamp is 0, returns null.
function toTimestamp(date) {
if (!date) {
if (date === undefined) {
return null
}

const timestamp = +date

// Not NaN.
// eslint-disable-next-line no-self-compare
if (timestamp === timestamp) {
return timestamp
try {
return parseDateTime(date)
} catch (error) {
warn('toTimestamp', { date, error })
return null
}

const ms = parseDateTime(date, 0)

return ms === 0 ? null : Math.round(ms / 1000)
}

// https://github.com/xenserver/xenadmin/blob/093ab0bcd6c4b3dd69da7b1e63ef34bb807c1ddb/XenModel/XenAPI-Extensions/VM.cs#L773-L827
Expand Down
3 changes: 1 addition & 2 deletions packages/xo-server/src/xapi-stats.mjs
Expand Up @@ -53,8 +53,7 @@ function convertNanToNull(value) {
}

async function getServerTimestamp(xapi, hostRef) {
const serverLocalTime = await xapi.call('host.get_servertime', hostRef)
return Math.floor(parseDateTime(serverLocalTime) / 1e3)
return parseDateTime(await xapi.call('host.get_servertime', hostRef))
}

// -------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion packages/xo-server/src/xapi/index.mjs
Expand Up @@ -1383,7 +1383,7 @@ export default class Xapi extends XapiBase {
}

async _getHostServerTimeShift(hostRef) {
return Math.abs(parseDateTime(await this.call('host.get_servertime', hostRef)) - Date.now())
return Math.abs(parseDateTime(await this.call('host.get_servertime', hostRef)) * 1e3 - Date.now())
}

async isHostServerTimeConsistent(hostRef) {
Expand Down
2 changes: 1 addition & 1 deletion packages/xo-server/src/xapi/mixins/patching.mjs
Expand Up @@ -549,7 +549,7 @@ export default {
await this.barrier(metricsRef)
await this._waitObjectState(metricsRef, metrics => metrics.live)

const getServerTime = async () => parseDateTime(await this.call('host.get_servertime', host.$ref))
const getServerTime = async () => parseDateTime(await this.call('host.get_servertime', host.$ref)) * 1e3
let rebootTime
if (isXcp) {
// On XCP-ng, install patches on each host one by one instead of all at once
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Expand Up @@ -18780,7 +18780,7 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"

test@^3.2.1:
test@^3.2.1, test@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/test/-/test-3.3.0.tgz#a2b56c6aa386c5732065793e8d9d92074a9cdd41"
integrity sha512-JKlEohxDIJRjwBH/+BrTcAPHljBALrAHw3Zs99RqZlaC605f6BggqXhxkdqZThbSHgaYPwpNJlf9bTSWkb/1rA==
Expand Down

0 comments on commit 473d091

Please sign in to comment.