Add placement audit commands

Bug #1793569 reported by Mohammed Naser
30
This bug affects 5 people
Affects Status Importance Assigned to Milestone
OpenStack Compute (nova)
Fix Released
Wishlist
Sylvain Bauza

Bug Description

It is possible that placement gets out of sync which can cause scheduling problems that would go unknown. I've built out this script would would be nice to have as `nova-manage placement audit`:

================================================================================
#!/usr/bin/env python

import argparse
import sys

from openstack import connection
import openstack.config

config = openstack.config.OpenStackConfig()
parser = argparse.ArgumentParser()
config.register_argparse_arguments(parser, sys.argv)

options = parser.parse_args()

cloud_region = config.get_one(argparse=options)
conn = connection.Connection(config=cloud_region)

# Grab list of all hypervisors and their servers
hypervisors = conn.compute.get('/os-hypervisors?with_servers=true', microversion='2.53').json().get('hypervisors')

# Generate a dictionary mapping of hypervisor => [instances]
hypervisor_mapping = {h['id']: [s['uuid'] for s in h.get('servers', [])] for h in hypervisors}
hypervisor_names = {h['id']: h['hypervisor_hostname'] for h in hypervisors}

# Grab list of all resource providers
resource_providers = conn.placement.get('/resource_providers').json().get('resource_providers')
for rp in resource_providers:
  # Check if RP has VCPU in inventory (i.e. compute node)
  inventories = conn.placement.get('/resource_providers/%s/inventories' % rp['uuid']).json().get('inventories')

  # Skip those without VCPU and MEMORY_MB (non computes)
  if 'MEMORY_MB' not in inventories and 'VCPU' not in inventories:
    continue

  # Get all allocations for RP
  allocations = conn.placement.get('/resource_providers/%s/allocations' % rp['uuid']).json().get('allocations')

  # Is there a compute node for this RP?
  if rp['uuid'] not in hypervisor_mapping:
    print "openstack resource provider delete %s # resource provider does not have matching provider" % rp['uuid']
    continue

  for allocation_id, info in allocations.iteritems():
    # The instance does not exist where placement says it should be.
    if allocation_id not in hypervisor_mapping[rp['uuid']]:
      hypervisor = None

      # Try to find where it's hiding.
      for hyp, instances in hypervisor_mapping.iteritems():
        if allocation_id in instances:
          hypervisor = hyp
          break

      # We found it.
      if hypervisor:
        classes = ','.join(["%s=%s" % (key, value) for key, value in info.get('resources').iteritems()])
        print "openstack resource provider allocation set --allocation rp=%s,%s %s # instance allocated on wrong rp" % (hypervisor, classes, allocation_id)
        continue

      # We don't know where this is. Let's see if it exists in Nova.
      server = conn.placement.get('/servers/%s' % allocation_id)
      if server.status_code == 404:
        print "openstack resource provider allocation delete %s # instance deleted" % allocation_id
        continue

      # TODO: idk? edge cases?
      raise
================================================================================

It would likely need to be rewritten to use the built-in placement HTTP client and objects to avoid extra API calls.

Revision history for this message
Matt Riedemann (mriedem) wrote :

Related tooling for cleaning up orphaned resources and fixing incorrect allocations:

https://github.com/larsks/os-placement-tools

Changed in nova:
importance: Undecided → Wishlist
status: New → Confirmed
tags: added: nova-manage placement
tags: added: ops
Revision history for this message
Mohammed Naser (mnaser) wrote :

Updated iteration that deletes allocations from RPs that has been deleted.

================
#!/usr/bin/env python

import argparse
import sys

from openstack import connection
import openstack.config

config = openstack.config.OpenStackConfig()
parser = argparse.ArgumentParser()
config.register_argparse_arguments(parser, sys.argv)

options = parser.parse_args()

cloud_region = config.get_one(argparse=options)
conn = connection.Connection(config=cloud_region)

# Grab list of all hypervisors and their servers
hypervisors = conn.compute.get('/os-hypervisors?with_servers=true', microversion='2.53').json().get('hypervisors')

# Generate a dictionary mapping of hypervisor => [instances]
hypervisor_mapping = {h['id']: [s['uuid'] for s in h.get('servers', [])] for h in hypervisors}
hypervisor_names = {h['id']: h['hypervisor_hostname'] for h in hypervisors}

# Grab list of all resource providers
resource_providers = conn.placement.get('/resource_providers').json().get('resource_providers')
for rp in resource_providers:
  # Check if RP has VCPU in inventory (i.e. compute node)
  inventories = conn.placement.get('/resource_providers/%s/inventories' % rp['uuid']).json().get('inventories')

  # Skip those without VCPU and MEMORY_MB (non computes)
  if 'MEMORY_MB' not in inventories and 'VCPU' not in inventories:
    continue

  # Get all allocations for RP
  allocations = conn.placement.get('/resource_providers/%s/allocations' % rp['uuid']).json().get('allocations')

  for allocation_id, info in allocations.iteritems():
    # No compute node for this rp, delete allocation
    if rp['uuid'] not in hypervisor_mapping:
      print "openstack resource provider allocation delete %s # resource provider does not have matching provider" % allocation_id
      continue

    # The instance does not exist where placement says it should be.
    if allocation_id not in hypervisor_mapping[rp['uuid']]:
      hypervisor = None

      # Try to find where it's hiding.
      for hyp, instances in hypervisor_mapping.iteritems():
        if allocation_id in instances:
          hypervisor = hyp
          break

      # We found it.
      if hypervisor:
        classes = ','.join(["%s=%s" % (key, value) for key, value in info.get('resources').iteritems()])
        print "openstack resource provider allocation set --allocation rp=%s,%s %s # instance allocated on wrong rp" % (hypervisor, classes, allocation_id)
        continue

      # We don't know where this is. Let's see if it exists in Nova.
      server = conn.placement.get('/servers/%s' % allocation_id)
      if server.status_code == 404:
        print "openstack resource provider allocation delete %s # instance deleted" % allocation_id
        continue

      # TODO: idk? edge cases?
      raise

  if rp['uuid'] not in hypervisor_mapping:
    print "openstack resource provider delete %s # resource provider does not have matching provider" % rp['uuid']
    continue

Revision history for this message
Chris Dent (cdent) wrote :
Download full text (3.7 KiB)

Somewhat related to this, some folks @vmware are making a script that talks to the database directly for some "placement audit" needs. Mostly because that's SOP for them and doesn't require dealing with tokens and such. I include it here as an example in case it is useful. This version is oriented towards a pre-nested world, mostly ocata, but sometimes queens.

-=-=-
#!/bin/bash

rp_count=0

rp_count=$(mysql -N -D nova_api -e "select count(id) from resource_providers;")

i=0
while [ $i -lt $rp_count ]
do

#Resource Provider ID
rp_id=$(mysql -N -D nova_api -e "select id from resource_providers limit $i, 1;")

#Resource Provider Name
rp_name=$(mysql -N -D nova_api -e "select name from resource_providers where id = $rp_id")

#Used Resources
cpu_used=$(mysql -N -D nova_api -e "select sum(used) from allocations where resource_class_id=0 and resource_provider_id=$rp_id;")
mem_used=$(mysql -N -D nova_api -e "select sum(used) from allocations where resource_class_id=1 and resource_provider_id=$rp_id;")
disk_used=$(mysql -N -D nova_api -e "select sum(used) from allocations where resource_class_id=2 and resource_provider_id=$rp_id;")

#Total Resources
cpu_total=$(mysql -N -D nova_api -e "select total from inventories where resource_class_id=0 and resource_provider_id=$rp_id;")
mem_total=$(mysql -N -D nova_api -e "select total from inventories where resource_class_id=1 and resource_provider_id=$rp_id;")
disk_total=$(mysql -N -D nova_api -e "select total from inventories where resource_class_id=2 and resource_provider_id=$rp_id;")

#Minimum Resource Value
cpu_min=$(mysql -N -D nova_api -e "select min_unit from inventories where resource_class_id=0 and resource_provider_id=$rp_id;")
mem_min=$(mysql -N -D nova_api -e "select min_unit from inventories where resource_class_id=1 and resource_provider_id=$rp_id;")
disk_min=$(mysql -N -D nova_api -e "select min_unit from inventories where resource_class_id=2 and resource_provider_id=$rp_id;")

#Maximum Resource Value
cpu_max=$(mysql -N -D nova_api -e "select max_unit from inventories where resource_class_id=0 and resource_provider_id=$rp_id;")
mem_max=$(mysql -N -D nova_api -e "select max_unit from inventories where resource_class_id=1 and resource_provider_id=$rp_id;")
disk_max=$(mysql -N -D nova_api -e "select max_unit from inventories where resource_class_id=2 and resource_provider_id=$rp_id;")

#Resource Step Size
cpu_step=$(mysql -N -D nova_api -e "select step_size from inventories where resource_class_id=0 and resource_provider_id=$rp_id;")
mem_step=$(mysql -N -D nova_api -e "select step_size from inventories where resource_class_id=1 and resource_provider_id=$rp_id;")
disk_step=$(mysql -N -D nova_api -e "select step_size from inventories where resource_class_id=2 and resource_provider_id=$rp_id;")

#Allocation Ratio
cpu_ratio=$(mysql -N -D nova_api -e "select allocation_ratio from inventories where resource_class_id=0 and resource_provider_id=$rp_id;")
mem_ratio=$(mysql -N -D nova_api -e "select allocation_ratio from inventories where resource_class_id=1 and resource_provider_id=$rp_id;")
disk_ratio=$(mysql -N -D nova_api -e "select allocation_ratio from inventories where resource_class_id=2 and resource...

Read more...

Revision history for this message
Matt Riedemann (mriedem) wrote :

Paste of mnaser's script: http://paste.openstack.org/show/734146/

Revision history for this message
Matt Riedemann (mriedem) wrote :

I think one thing that larsks' and mnaser's audit scripts probably aren't handling are allocations held by migration records which never got cleaned up when a migration completed, likely failed for some reason and weren't cleaned up, e.g. we have several bugs related to failed migrations not cleaning up allocations properly:

https://review.opendev.org/#/c/654067/

https://review.opendev.org/#/c/663737/

https://review.opendev.org/#/q/topic:bug/1821594+(status:open+OR+status:merged)

The "nova-manage placement heal_allocations" CLI could probably be updated to deal with at least reporting allocations held by migration records that are not in progress so operators could delete those allocations or at least investigate the related instance for the migration to make sure it's allocations are OK, i.e. the operator could delete the allocations for both the migration consumer and instance consumer and then run the heal_allocations CLI again to re-add the allocations for just the instance on its current compute node resource provider.

Changed in nova:
assignee: nobody → Sylvain Bauza (sylvain-bauza)
Revision history for this message
Matt Riedemann (mriedem) wrote :

Per comment 5, I think I meant to say:

The "nova-manage placement audit" CLI could probably be updated to deal with at least reporting allocations held by migration records...

since heal_allocations doesn't report on things really, nor does it delete allocations, it only adds allocations for instances (not migrations) that are missing.

Revision history for this message
Matt Riedemann (mriedem) wrote :

(10:07:47 AM) mriedem: bauzas: i think what i was thinking of was an audit command could detect that you have orphaned allocations tied to a not-in-progress migration, e.g. a migration that failed but we failed to cleanup the allocations,
(10:08:20 AM) mriedem: bauzas: and then that information could be provided to the admin to then determine what to do, e.g. delete the allocations for the migration record consumer and potentially the related instance,
(10:08:33 AM) bauzas: mriedem: yeah ok
(10:08:35 AM) mriedem: and if they delete the allocations for the instance, then they could run heal_allocations on the instance to fix things up
(10:08:49 AM) mriedem: we could also eventually build on that to make it automatic with options
(10:08:57 AM) mriedem: e.g. nova-manage placement audit --heal
(10:09:00 AM) mriedem: something like that

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to nova (master)

Fix proposed to branch: master
Review: https://review.opendev.org/670112

Changed in nova:
status: Confirmed → In Progress
Revision history for this message
Matt Riedemann (mriedem) wrote :

Bug 1836369 gives another scenario where allocations can be leaked and not tracked correctly when compute crashes during a resize.

Changed in nova:
assignee: Sylvain Bauza (sylvain-bauza) → sean mooney (sean-k-mooney)
Changed in nova:
assignee: sean mooney (sean-k-mooney) → Sylvain Bauza (sylvain-bauza)
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Related fix proposed to nova (master)

Related fix proposed to branch: master
Review: https://review.opendev.org/715797

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix merged to nova (master)

Reviewed: https://review.opendev.org/670112
Committed: https://git.openstack.org/cgit/openstack/nova/commit/?id=c03716be1f3e9fd5a5e9adf95860c7ce4bfe211f
Submitter: Zuul
Branch: master

commit c03716be1f3e9fd5a5e9adf95860c7ce4bfe211f
Author: Sylvain Bauza <email address hidden>
Date: Wed Jul 10 18:11:23 2019 +0200

    Add a placement audit command

    There are different situations when allocations can be orphaned.
    Adding a new nova-manage command to lookup at all resource providers
    and check against the related compute nodes whether they have
    orphaned allocations.

    Change-Id: I537ed74503d208957f0a97af3ab754a6750dac20
    Closes-Bug: #1793569

Changed in nova:
status: In Progress → Fix Released
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Related fix merged to nova (master)

Reviewed: https://review.opendev.org/715797
Committed: https://git.openstack.org/cgit/openstack/nova/commit/?id=35ec5a0bd1d82d4d973bdfe1e62dc5a795eb533a
Submitter: Zuul
Branch: master

commit 35ec5a0bd1d82d4d973bdfe1e62dc5a795eb533a
Author: Sylvain Bauza <email address hidden>
Date: Mon Mar 30 09:19:44 2020 +0200

    FUP for Add a placement audit command

    Since I537ed74503d208957f0a97af3ab754a6750dac20 had some clean-up comments,
    we can just provide a follow-up change.

    Change-Id: Ie8b5147322e13ad7df966b5c3c41ef0418e4f64c
    Related-Bug: #1793569

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to nova (stable/train)

Fix proposed to branch: stable/train
Review: https://review.opendev.org/720838

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to nova (stable/stein)

Fix proposed to branch: stable/stein
Review: https://review.opendev.org/720839

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to nova (stable/rocky)

Fix proposed to branch: stable/rocky
Review: https://review.opendev.org/720842

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to nova (stable/queens)

Fix proposed to branch: stable/queens
Review: https://review.opendev.org/723751

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Change abandoned on nova (stable/train)

Change abandoned by Artom Lifshitz (<email address hidden>) on branch: stable/train
Review: https://review.opendev.org/720838

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Change abandoned on nova (stable/stein)

Change abandoned by Artom Lifshitz (<email address hidden>) on branch: stable/stein
Review: https://review.opendev.org/720839

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Change abandoned on nova (stable/rocky)

Change abandoned by Artom Lifshitz (<email address hidden>) on branch: stable/rocky
Review: https://review.opendev.org/720842

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Change abandoned on nova (stable/queens)

Change abandoned by Artom Lifshitz (<email address hidden>) on branch: stable/queens
Review: https://review.opendev.org/723751

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.