<commit_before>const CancelToken = require('promise-toolbox/CancelToken')
const defer = require('golike-defer').default
const groupBy = require('lodash/groupBy')
const pickBy = require('lodash/pickBy')
const ignoreErrors = require('promise-toolbox/ignoreErrors')
const omit = require('lodash/omit')
const pCatch = require('promise-toolbox/catch')
const pRetry = require('promise-toolbox/retry')
const { asyncMap } = require('@xen-orchestra/async-map')
const { createLogger } = require('@xen-orchestra/log')
const { incorrectState } = require('xo-common/api-errors')
const { Ref } = require('xen-api')

const extractOpaqueRef = require('./_extractOpaqueRef')
const isDefaultTemplate = require('./isDefaultTemplate')
const isVmRunning = require('./_isVmRunning')

const { warn } = createLogger('xo:xapi:vm')

const BIOS_STRINGS_KEYS = new Set([
  'baseboard-asset-tag',
  'baseboard-location-in-chassis',
  'baseboard-manufacturer',
  'baseboard-product-name',
  'baseboard-serial-number',
  'baseboard-version',
  'bios-vendor',
  'bios-version',
  'enclosure-asset-tag',
  'system-manufacturer',
  'system-product-name',
  'system-serial-number',
  'system-version',
])
const cleanBiosStrings = biosStrings => {
  if (biosStrings !== undefined) {
    biosStrings = pickBy(biosStrings, (value, key) => value !== '' && BIOS_STRINGS_KEYS.has(key))

    if (Object.keys(biosStrings).length !== 0) {
      return biosStrings
    }
  }
}

async function safeGetRecord(xapi, type, ref) {
  try {
    return await xapi.getRecord(type, ref)
  } catch (_) {
    return ref
  }
}

const noop = Function.prototype

module.exports = class Vm {
  async _assertHealthyVdiChain(vdiRefOrUuid, cache, tolerance) {
    let vdi = cache[vdiRefOrUuid]
    if (vdi === undefined) {
      try {
        vdi = await this[vdiRefOrUuid.startsWith('OpaqueRef:') ? 'getRecord' : 'getRecordByUuid']('VDI', vdiRefOrUuid)
      } catch (error) {
        warn(error)
        return
      }
      cache[vdi.$ref] = vdi
      cache[vdi.uuid] = vdi
    }

    if (!vdi.managed) {
      const srRef = vdi.SR
      let childrenMap = cache[srRef]
      if (childrenMap === undefined) {
        const vdiRefs = await this.getField('SR', srRef, 'VDIs')
        childrenMap = groupBy(
          (
            await Promise.all(
              vdiRefs.map(async vdiRef => {
                let vdi = cache[vdiRef]
                if (vdi === undefined) {
                  try {
                    vdi = await this.getRecord('VDI', vdiRef)
                  } catch (error) {
                    warn(error)
                    return
                  }
                  cache[vdiRef] = vdi
                  cache[vdi.uuid] = vdi
                }
                return vdi
              })
            )
          ).filter(_ => _ !== undefined),
          vdi => vdi.sm_config['vhd-parent']
        )
      }

      // an unmanaged VDI should not have exactly one child: they
      // should coalesce
      const children = childrenMap[vdi.uuid]
      if (
        children.length === 1 &&
        !children[0].managed && // some SRs do not coalesce the leaf
        tolerance-- <= 0
      ) {
        throw new Error('unhealthy VDI chain')
      }
    }

    const parentUuid = vdi.sm_config['vhd-parent']
    if (parentUuid !== undefined) {
      return this._assertHealthyVdiChain(parentUuid, cache, tolerance)
    }
  }

  async assertHealthyVdiChains(vmRef, tolerance = this._maxUncoalescedVdis) {
    const vdiRefs = {}
    ;(await this.getRecords('VBD', await this.getField('VM', vmRef, 'VBDs'))).forEach(({ VDI: ref }) => {
      if (Ref.isNotEmpty(ref)) {
        vdiRefs[ref] = true
      }
    })
    const cache = { __proto__: null }
    for (const vdiRef of Object.keys(vdiRefs)) {
      await this._assertHealthyVdiChain(vdiRef, cache, tolerance)
    }
  }

  async checkpoint(vmRef, { cancelToken = CancelToken.none, name_label } = {}) {
    if (name_label === undefined) {
      name_label = await this.getField('VM', vmRef, 'name_label')
    }
    try {
      return await this.callAsync(cancelToken, 'VM.checkpoint', vmRef, name_label).then(extractOpaqueRef)
    } catch (error) {
      if (error.code === 'VM_BAD_POWER_STATE') {
        return this.VM_snapshot(vmRef, { cancelToken, name_label })
      }
      throw error
    }
  }

  @defer
  async create(
    $defer,
    {
      actions_after_crash = 'reboot',
      actions_after_reboot = 'reboot',
      actions_after_shutdown = 'destroy',
      affinity = Ref.EMPTY,
      appliance,
      blocked_operations,
      domain_type,
      generation_id,
      ha_restart_priority,
      hardware_platform_version,
      has_vendor_device = false, // Avoid issue with some Dundee builds.
      HVM_boot_params,
      HVM_boot_policy,
      HVM_shadow_multiplier,
      is_a_template = false,
      is_vmss_snapshot,
      last_boot_CPU_flags, // Used when the VM is created Suspended
      last_booted_record, // Used when the VM is created Suspended
      memory_static_max,
      memory_static_min,
      name_description,
      name_label,
      // NVRAM, // experimental
      order,<commit_msg>fix(xapi/VM_create): default actions_after_{crash,reboot} is `restart`<commit_after>const CancelToken = require('promise-toolbox/CancelToken')
const defer = require('golike-defer').default
const groupBy = require('lodash/groupBy')
const pickBy = require('lodash/pickBy')
const ignoreErrors = require('promise-toolbox/ignoreErrors')
const omit = require('lodash/omit')
const pCatch = require('promise-toolbox/catch')
const pRetry = require('promise-toolbox/retry')
const { asyncMap } = require('@xen-orchestra/async-map')
const { createLogger } = require('@xen-orchestra/log')
const { incorrectState } = require('xo-common/api-errors')
const { Ref } = require('xen-api')

const extractOpaqueRef = require('./_extractOpaqueRef')
const isDefaultTemplate = require('./isDefaultTemplate')
const isVmRunning = require('./_isVmRunning')

const { warn } = createLogger('xo:xapi:vm')

const BIOS_STRINGS_KEYS = new Set([
  'baseboard-asset-tag',
  'baseboard-location-in-chassis',
  'baseboard-manufacturer',
  'baseboard-product-name',
  'baseboard-serial-number',
  'baseboard-version',
  'bios-vendor',
  'bios-version',
  'enclosure-asset-tag',
  'system-manufacturer',
  'system-product-name',
  'system-serial-number',
  'system-version',
])
const cleanBiosStrings = biosStrings => {
  if (biosStrings !== undefined) {
    biosStrings = pickBy(biosStrings, (value, key) => value !== '' && BIOS_STRINGS_KEYS.has(key))

    if (Object.keys(biosStrings).length !== 0) {
      return biosStrings
    }
  }
}

async function safeGetRecord(xapi, type, ref) {
  try {
    return await xapi.getRecord(type, ref)
  } catch (_) {
    return ref
  }
}

const noop = Function.prototype

module.exports = class Vm {
  async _assertHealthyVdiChain(vdiRefOrUuid, cache, tolerance) {
    let vdi = cache[vdiRefOrUuid]
    if (vdi === undefined) {
      try {
        vdi = await this[vdiRefOrUuid.startsWith('OpaqueRef:') ? 'getRecord' : 'getRecordByUuid']('VDI', vdiRefOrUuid)
      } catch (error) {
        warn(error)
        return
      }
      cache[vdi.$ref] = vdi
      cache[vdi.uuid] = vdi
    }

    if (!vdi.managed) {
      const srRef = vdi.SR
      let childrenMap = cache[srRef]
      if (childrenMap === undefined) {
        const vdiRefs = await this.getField('SR', srRef, 'VDIs')
        childrenMap = groupBy(
          (
            await Promise.all(
              vdiRefs.map(async vdiRef => {
                let vdi = cache[vdiRef]
                if (vdi === undefined) {
                  try {
                    vdi = await this.getRecord('VDI', vdiRef)
                  } catch (error) {
                    warn(error)
                    return
                  }
                  cache[vdiRef] = vdi
                  cache[vdi.uuid] = vdi
                }
                return vdi
              })
            )
          ).filter(_ => _ !== undefined),
          vdi => vdi.sm_config['vhd-parent']
        )
      }

      // an unmanaged VDI should not have exactly one child: they
      // should coalesce
      const children = childrenMap[vdi.uuid]
      if (
        children.length === 1 &&
        !children[0].managed && // some SRs do not coalesce the leaf
        tolerance-- <= 0
      ) {
        throw new Error('unhealthy VDI chain')
      }
    }

    const parentUuid = vdi.sm_config['vhd-parent']
    if (parentUuid !== undefined) {
      return this._assertHealthyVdiChain(parentUuid, cache, tolerance)
    }
  }

  async assertHealthyVdiChains(vmRef, tolerance = this._maxUncoalescedVdis) {
    const vdiRefs = {}
    ;(await this.getRecords('VBD', await this.getField('VM', vmRef, 'VBDs'))).forEach(({ VDI: ref }) => {
      if (Ref.isNotEmpty(ref)) {
        vdiRefs[ref] = true
      }
    })
    const cache = { __proto__: null }
    for (const vdiRef of Object.keys(vdiRefs)) {
      await this._assertHealthyVdiChain(vdiRef, cache, tolerance)
    }
  }

  async checkpoint(vmRef, { cancelToken = CancelToken.none, name_label } = {}) {
    if (name_label === undefined) {
      name_label = await this.getField('VM', vmRef, 'name_label')
    }
    try {
      return await this.callAsync(cancelToken, 'VM.checkpoint', vmRef, name_label).then(extractOpaqueRef)
    } catch (error) {
      if (error.code === 'VM_BAD_POWER_STATE') {
        return this.VM_snapshot(vmRef, { cancelToken, name_label })
      }
      throw error
    }
  }

  @defer
  async create(
    $defer,
    {
      actions_after_crash = 'restart',
      actions_after_reboot = 'restart',
      actions_after_shutdown = 'destroy',
      affinity = Ref.EMPTY,
      appliance,
      blocked_operations,
      domain_type,
      generation_id,
      ha_restart_priority,
      hardware_platform_version,
      has_vendor_device = false, // Avoid issue with some Dundee builds.
      HVM_boot_params,
      HVM_boot_policy,
      HVM_shadow_multiplier,
      is_a_template = false,
      is_vmss_snapshot,
      last_boot_CPU_flags, // Used when the VM is created Suspended
      last_booted_record, // Used when the VM is created Suspended
      memory_static_max,
      memory_static_min,
      name_description,
      name_label,
      // NVRAM, // experimental
      order,
{'fraction_non_alphanumeric': 0.12409183376925312, 'fraction_numerical': 0.0007749685169040007, 'mean_word_length': 2.53077975376197, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4200871', 'n_tokens_mistral': 3397, 'n_tokens_neox': 3344, 'n_words': 671}