[Bugfix] 修复Hostname可能不唯一引起的任务执行失败

pull/1698/head
ibuler 2018-08-16 12:44:39 +08:00
parent 5b93a1a0a5
commit 2ac5786ba1
8 changed files with 113 additions and 34 deletions

View File

@ -48,7 +48,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
return return
node = get_object_or_404(Node, id=node_id) node = get_object_or_404(Node, id=node_id)
show_current_asset = self.request.query_params.get("show_current_asset") show_current_asset = self.request.query_params.get("show_current_asset") in ('1', 'true')
if node.is_root(): if node.is_root():
if show_current_asset: if show_current_asset:

View File

@ -6,8 +6,10 @@ import uuid
import logging import logging
import random import random
from functools import reduce from functools import reduce
from collections import defaultdict
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache from django.core.cache import cache
@ -162,11 +164,19 @@ class Asset(OrgModelMixin):
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes)) nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
return nodes return nodes
@property @classmethod
def org_name(self): def get_queryset_by_fullname_list(cls, fullname_list):
from orgs.models import Organization org_fullname_map = defaultdict(list)
org = Organization.get_instance(self.org_id) for fullname in fullname_list:
return org.name hostname, org = cls.split_fullname(fullname)
org_fullname_map[org].append(hostname)
filter_arg = Q()
for org, hosts in org_fullname_map.items():
if org.is_real():
filter_arg |= Q(hostname__in=hosts, org_id=org.id)
else:
filter_arg |= Q(Q(org_id__isnull=True) | Q(org_id=''), hostname__in=hosts)
return Asset.objects.filter(filter_arg)
@property @property
def hardware_info(self): def hardware_info(self):

View File

@ -35,4 +35,4 @@ class Label(OrgModelMixin):
class Meta: class Meta:
db_table = "assets_label" db_table = "assets_label"
unique_together = [('name', 'value')] unique_together = [('name', 'value', 'org_id')]

View File

@ -44,7 +44,7 @@ def set_assets_hardware_info(result, **kwargs):
logger.error("Get asset info failed: {}".format(hostname)) logger.error("Get asset info failed: {}".format(hostname))
continue continue
asset = get_object_or_none(Asset, hostname=hostname) asset = Asset.objects.get_object_by_fullname(hostname)
if not asset: if not asset:
continue continue
@ -96,7 +96,7 @@ def update_assets_hardware_info_util(assets, task_name=None):
# task_name = _("Update some assets hardware info") # task_name = _("Update some assets hardware info")
task_name = _("更新资产硬件信息") task_name = _("更新资产硬件信息")
tasks = const.UPDATE_ASSETS_HARDWARE_TASKS tasks = const.UPDATE_ASSETS_HARDWARE_TASKS
hostname_list = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()] hostname_list = [asset.fullname for asset in assets if asset.is_active and asset.is_unixlike()]
if not hostname_list: if not hostname_list:
logger.info("Not hosts get, may be asset is not active or not unixlike platform") logger.info("Not hosts get, may be asset is not active or not unixlike platform")
return {} return {}
@ -135,7 +135,7 @@ def update_assets_hardware_info_period():
# task_name = _("Update assets hardware info period") # task_name = _("Update assets hardware info period")
task_name = _("定期更新资产硬件信息") task_name = _("定期更新资产硬件信息")
hostname_list = [ hostname_list = [
asset.hostname for asset in Asset.objects.all() asset.fullname for asset in Asset.objects.all()
if asset.is_active and asset.is_unixlike() if asset.is_active and asset.is_unixlike()
] ]
tasks = const.UPDATE_ASSETS_HARDWARE_TASKS tasks = const.UPDATE_ASSETS_HARDWARE_TASKS
@ -182,7 +182,7 @@ def test_admin_user_connectability_util(admin_user, task_name):
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
assets = admin_user.get_related_assets() assets = admin_user.get_related_assets()
hosts = [asset.hostname for asset in assets hosts = [asset.fullname for asset in assets
if asset.is_active and asset.is_unixlike()] if asset.is_active and asset.is_unixlike()]
if not hosts: if not hosts:
return return
@ -229,7 +229,7 @@ def test_asset_connectability_util(assets, task_name=None):
if task_name is None: if task_name is None:
# task_name = _("Test assets connectability") # task_name = _("Test assets connectability")
task_name = _("测试资产可连接性") task_name = _("测试资产可连接性")
hosts = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()] hosts = [asset.fullname for asset in assets if asset.is_active and asset.is_unixlike()]
if not hosts: if not hosts:
logger.info("No hosts, passed") logger.info("No hosts, passed")
return {} return {}
@ -281,7 +281,7 @@ def test_system_user_connectability_util(system_user, task_name):
""" """
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
assets = system_user.get_assets() assets = system_user.get_assets()
hosts = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()] hosts = [asset.fullname for asset in assets if asset.is_active and asset.is_unixlike()]
tasks = const.TEST_SYSTEM_USER_CONN_TASKS tasks = const.TEST_SYSTEM_USER_CONN_TASKS
if not hosts: if not hosts:
logger.info("No hosts, passed") logger.info("No hosts, passed")
@ -379,7 +379,7 @@ def push_system_user_util(system_users, assets, task_name):
logger.info("Not tasks, passed") logger.info("Not tasks, passed")
return {} return {}
hosts = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()] hosts = [asset.fullname for asset in assets if asset.is_active and asset.is_unixlike()]
if not hosts: if not hosts:
logger.info("Not hosts, passed") logger.info("Not hosts, passed")
return {} return {}

View File

@ -12,8 +12,8 @@ def get_assets_by_id_list(id_list):
return Asset.objects.filter(id__in=id_list) return Asset.objects.filter(id__in=id_list)
def get_assets_by_hostname_list(hostname_list): def get_assets_by_fullname_list(hostname_list):
return Asset.objects.filter(hostname__in=hostname_list) return Asset.objects.get_queryset_by_fullname_list(hostname_list)
def get_system_user_by_name(name): def get_system_user_by_name(name):

View File

@ -2,7 +2,7 @@
# #
from .ansible.inventory import BaseInventory from .ansible.inventory import BaseInventory
from assets.utils import get_assets_by_hostname_list, get_system_user_by_name from assets.utils import get_assets_by_fullname_list, get_system_user_by_name
__all__ = [ __all__ = [
'JMSInventory' 'JMSInventory'
@ -44,7 +44,7 @@ class JMSInventory(BaseInventory):
super().__init__(host_list=host_list) super().__init__(host_list=host_list)
def get_jms_assets(self): def get_jms_assets(self):
assets = get_assets_by_hostname_list(self.hostname_list) assets = get_assets_by_fullname_list(self.hostname_list)
return assets return assets
def convert_to_ansible(self, asset, run_as_admin=False): def convert_to_ansible(self, asset, run_as_admin=False):

View File

@ -8,7 +8,7 @@ from django.shortcuts import redirect
from django.forms import ModelForm from django.forms import ModelForm
from django.http.response import HttpResponseForbidden from django.http.response import HttpResponseForbidden
from common.utils import get_logger from common.utils import get_logger, is_uuid
from .utils import current_org, set_current_org, set_to_root_org from .utils import current_org, set_current_org, set_to_root_org
from .models import Organization from .models import Organization
@ -39,6 +39,25 @@ class OrgManager(models.Manager):
tl.times += 1 tl.times += 1
return queryset return queryset
def filter_by_fullname(self, fullname, field=None):
ori_org = current_org
value, org = self.model.split_fullname(fullname)
set_current_org(org)
if not field:
if hasattr(self.model, 'name'):
field = 'name'
elif hasattr(self.model, 'hostname'):
field = 'hostname'
queryset = self.get_queryset().filter(**{field: value})
set_current_org(ori_org)
return queryset
def get_object_by_fullname(self, fullname, field=None):
queryset = self.filter_by_fullname(fullname, field=field)
if len(queryset) == 1:
return queryset[0]
return None
def all(self): def all(self):
if not current_org: if not current_org:
msg = 'You can `objects.set_current_org(org).all()` then run it' msg = 'You can `objects.set_current_org(org).all()` then run it'
@ -57,11 +76,50 @@ class OrgModelMixin(models.Model):
org_id = models.CharField(max_length=36, null=True, blank=True, default=None) org_id = models.CharField(max_length=36, null=True, blank=True, default=None)
objects = OrgManager() objects = OrgManager()
sep = '@'
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if current_org and current_org.is_real(): if current_org and current_org.is_real():
self.org_id = current_org.id self.org_id = current_org.id
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
@classmethod
def split_fullname(cls, fullname, sep=None):
if not sep:
sep = cls.sep
index = fullname.rfind(sep)
if index == -1:
value = fullname
org = Organization.default()
else:
value = fullname[:index]
org = Organization.get_instance(fullname[index + 1:])
return value, org
@property
def org(self):
from orgs.models import Organization
org = Organization.get_instance(self.org_id)
return org
@property
def org_name(self):
return self.org.name
@property
def fullname(self, attr=None):
name = ''
if attr and hasattr(self, attr):
name = getattr(self, attr)
elif hasattr(self, 'name'):
name = self.name
elif hasattr(self, 'hostname'):
name = self.hostname
if self.org.is_real():
return name + self.sep + self.org_name
else:
return name
class Meta: class Meta:
abstract = True abstract = True

View File

@ -4,6 +4,8 @@ from django.db import models
from django.core.cache import cache from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import is_uuid
class Organization(models.Model): class Organization(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
@ -15,19 +17,23 @@ class Organization(models.Model):
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment')) comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
CACHE_PREFIX = 'JMS_ORG_{}' CACHE_PREFIX = 'JMS_ORG_{}'
ROOT_ID = 'ROOT' ROOT_ID_NAME = 'ROOT'
DEFAULT_ID = 'DEFAULT' DEFAULT_ID_NAME = 'DEFAULT'
def __str__(self): def __str__(self):
return self.name return self.name
def set_to_cache(self): def set_to_cache(self):
key = self.CACHE_PREFIX.format(self.id) key_id = self.CACHE_PREFIX.format(self.id)
cache.set(key, self, 3600) key_name = self.CACHE_PREFIX.format(self.name)
cache.set(key_id, self, 3600)
cache.set(key_name, self, 3600)
def expire_cache(self): def expire_cache(self):
key = self.CACHE_PREFIX.format(self.id) key_id = self.CACHE_PREFIX.format(self.id)
cache.set(key, self, 1) key_name = self.CACHE_PREFIX.format(self.name)
cache.delete(key_id)
cache.delete(key_name)
@classmethod @classmethod
def get_instance_from_cache(cls, oid): def get_instance_from_cache(cls, oid):
@ -35,18 +41,23 @@ class Organization(models.Model):
return cache.get(key, None) return cache.get(key, None)
@classmethod @classmethod
def get_instance(cls, oid, default=True): def get_instance(cls, id_or_name, default=True):
cached = cls.get_instance_from_cache(oid) cached = cls.get_instance_from_cache(id_or_name)
if cached: if cached:
return cached return cached
if oid == cls.DEFAULT_ID: if not id_or_name:
return cls.default() if default else None
elif id_or_name == cls.DEFAULT_ID_NAME:
return cls.default() return cls.default()
elif oid == cls.ROOT_ID: elif id_or_name == cls.ROOT_ID_NAME:
return cls.root() return cls.root()
try: try:
org = cls.objects.get(id=oid) if is_uuid(id_or_name):
org = cls.objects.get(id=id_or_name)
else:
org = cls.objects.get(name=id_or_name)
org.set_to_cache() org.set_to_cache()
except cls.DoesNotExist: except cls.DoesNotExist:
org = cls.default() if default else None org = cls.default() if default else None
@ -92,20 +103,20 @@ class Organization(models.Model):
@classmethod @classmethod
def default(cls): def default(cls):
return cls(id=cls.DEFAULT_ID, name="Default") return cls(id=cls.DEFAULT_ID_NAME, name=cls.DEFAULT_ID_NAME)
@classmethod @classmethod
def root(cls): def root(cls):
return cls(id=cls.ROOT_ID, name='Root') return cls(id=cls.ROOT_ID_NAME, name=cls.ROOT_ID_NAME)
def is_root(self): def is_root(self):
if self.id is self.ROOT_ID: if self.id is self.ROOT_ID_NAME:
return True return True
else: else:
return False return False
def is_default(self): def is_default(self):
if self.id is self.DEFAULT_ID: if self.id is self.DEFAULT_ID_NAME:
return True return True
else: else:
return False return False