Initial
This commit is contained in:
@@ -535,7 +535,7 @@ target hosts visible to the operator who creates or edits them.
|
||||
|
||||
VMware vCenter import uses the effective Config / Integrations values (`vcenter_*` settings or the `VCENTER_*` environment variables). `vcenter_api_mode` can be `auto`, `api`, or `rest`. `api` uses the current vSphere Automation API flow (`POST /api/session`, `GET /api/vcenter/vm`). `rest` uses the legacy/community vCenter REST flow (`POST /rest/com/vmware/cis/session`, `GET /rest/vcenter/vm`). `auto` tries the current `/api` profile first and falls back to `/rest` when the endpoint is not supported.
|
||||
|
||||
For exact VM-name searches (`field: "name"`, `operator: "equals"`), POSHManager uses the documented server-side name filters: `names=<vm>` for `/api/vcenter/vm` and `filter.names=<vm>` for `/rest/vcenter/vm`. Contains/starts-with/ends-with searches are filtered locally because vCenter VM list filters do not provide wildcard contains semantics. The combined `hostname` field searches both the VM inventory name and the VMware Tools guest `host_name`/FQDN after enrichment. FQDN and IP discovery depends on VMware Tools data; when guest data is unavailable, the import falls back to the VM name as the host address and records the limitation in host notes.
|
||||
For exact VM-name searches (`field: "name"`, `operator: "equals"`), POSHManager uses the documented server-side name filters: `names=<vm>` for `/api/vcenter/vm` and `filter.names=<vm>` for `/rest/vcenter/vm`. Contains/starts-with/ends-with searches are filtered locally because vCenter VM list filters do not provide wildcard contains semantics. The combined `hostname` field searches both the VM inventory name and the VMware Tools guest `host_name`/FQDN after enrichment. VM display-name matches are checked first, then the rest of the inventory is still enriched for guest-only hostname matches. Networking/IP enrichment is only requested for candidates that match the filter. FQDN and IP discovery depends on VMware Tools data; when guest data is unavailable, the import falls back to the VM name as the host address and records the limitation in host notes.
|
||||
|
||||
For `field: "name"` filters, the API scans the full VM summary list returned by vCenter before guest enrichment, so large inventories are not accidentally missed by the preview limit. The preview response includes `diagnostics` with `totalFromVCenter`, `summaryMatched`, `scanned`, `resultLimit`, and `sampleNames`; the wizard displays those values when troubleshooting a no-result preview.
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ const WINDOWS_HINTS = ['windows', 'microsoft'];
|
||||
const LINUX_HINTS = ['linux', 'ubuntu', 'debian', 'red hat', 'rhel', 'centos', 'suse', 'oracle linux'];
|
||||
const MAX_TRACE_ENTRIES = 700;
|
||||
const MAX_TRACE_BODY_CHARS = 6000;
|
||||
const VM_ENRICHMENT_CONCURRENCY = 8;
|
||||
const API_PROFILES = {
|
||||
api: {
|
||||
mode: 'api',
|
||||
@@ -319,8 +320,14 @@ export function filterSummaryVms(vms = [], filter = {}) {
|
||||
if (!['name', 'hostname'].includes(field)) return { vms, summaryMatched: vms.length, summaryFilterApplied: false, summaryFilterFallback: false };
|
||||
const summaryFilter = { ...filter, field: 'name' };
|
||||
const matched = vms.filter((vm) => vmMatchesFilter(normalizeVCenterSummaryVm(vm), summaryFilter));
|
||||
if (field === 'hostname' && !matched.length) {
|
||||
return { vms, summaryMatched: 0, summaryFilterApplied: true, summaryFilterFallback: true };
|
||||
if (field === 'hostname') {
|
||||
const matchedIds = new Set(matched.map((vm) => vm.vm || vm.id));
|
||||
return {
|
||||
vms: [...matched, ...vms.filter((vm) => !matchedIds.has(vm.vm || vm.id))],
|
||||
summaryMatched: matched.length,
|
||||
summaryFilterApplied: true,
|
||||
summaryFilterFallback: matched.length === 0
|
||||
};
|
||||
}
|
||||
return { vms: matched, summaryMatched: matched.length, summaryFilterApplied: true, summaryFilterFallback: false };
|
||||
}
|
||||
@@ -385,16 +392,17 @@ export async function previewVCenterHosts(options = {}) {
|
||||
filter: options.filter || {}
|
||||
};
|
||||
|
||||
for (const vm of vms) {
|
||||
const vmId = vm.vm || vm.id;
|
||||
if (!vmId) continue;
|
||||
diagnostics.scanned += 1;
|
||||
const identity = await optionalVCenterFetch(settings, profile.guestIdentityPath(vmId), sessionId, trace);
|
||||
const interfaces = await optionalVCenterFetch(settings, profile.guestNetworkingPath(vmId), sessionId, trace);
|
||||
const candidate = normalizeVCenterVm(vm, identity?.unavailable ? null : identity, interfaces?.unavailable ? null : interfaces);
|
||||
if (vmMatchesFilter(candidate, options.filter)) candidates.push(candidate);
|
||||
if (candidates.length >= (options.limit || 100)) break;
|
||||
}
|
||||
await enrichVmsForPreview({
|
||||
vms,
|
||||
settings,
|
||||
profile,
|
||||
sessionId,
|
||||
trace,
|
||||
filter: options.filter,
|
||||
limit: options.limit || 100,
|
||||
diagnostics,
|
||||
candidates
|
||||
});
|
||||
|
||||
return {
|
||||
status: vcenterStatus(),
|
||||
@@ -406,6 +414,28 @@ export async function previewVCenterHosts(options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
async function enrichVmsForPreview({ vms, settings, profile, sessionId, trace, filter, limit, diagnostics, candidates }) {
|
||||
let index = 0;
|
||||
async function worker() {
|
||||
while (index < vms.length && candidates.length < limit) {
|
||||
const vm = vms[index++];
|
||||
const vmId = vm.vm || vm.id;
|
||||
if (!vmId) continue;
|
||||
diagnostics.scanned += 1;
|
||||
const identity = await optionalVCenterFetch(settings, profile.guestIdentityPath(vmId), sessionId, trace);
|
||||
const candidateWithoutNetworking = normalizeVCenterVm(vm, identity?.unavailable ? null : identity, null);
|
||||
if (!vmMatchesFilter(candidateWithoutNetworking, filter)) continue;
|
||||
const interfaces = await optionalVCenterFetch(settings, profile.guestNetworkingPath(vmId), sessionId, trace);
|
||||
const candidate = normalizeVCenterVm(vm, identity?.unavailable ? null : identity, interfaces?.unavailable ? null : interfaces);
|
||||
if (candidates.length < limit) candidates.push(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
Array.from({ length: Math.min(VM_ENRICHMENT_CONCURRENCY, Math.max(vms.length, 1)) }, () => worker())
|
||||
);
|
||||
}
|
||||
|
||||
function shouldTryFallback(err) {
|
||||
return err instanceof VCenterTraceError && [404, 405, 501].includes(err.statusCode);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ test('filterSummaryVms prefilters hostname searches by VM name and falls back wh
|
||||
|
||||
assert.equal(matched.summaryMatched, 1);
|
||||
assert.equal(matched.summaryFilterFallback, false);
|
||||
assert.deepEqual(matched.vms.map((vm) => vm.vm), ['vm-1']);
|
||||
assert.deepEqual(matched.vms.map((vm) => vm.vm), ['vm-1', 'vm-2']);
|
||||
assert.equal(fallback.summaryMatched, 0);
|
||||
assert.equal(fallback.summaryFilterFallback, true);
|
||||
assert.equal(fallback.vms.length, 2);
|
||||
|
||||
Reference in New Issue
Block a user