Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Commit

Permalink
Remove fk relation between lustreclientmount and fs (#1931)
Browse files Browse the repository at this point in the history
We may learn about a lustreclientmount prior to learning about a fs. We
should remove the fk association so we can keep mounts before we have a fs.

Signed-off-by: Igor Pashev <pashev.igor@gmail.com>
  • Loading branch information
ip1981 authored Jun 3, 2020
1 parent 03c9eb5 commit f10ba6b
Show file tree
Hide file tree
Showing 13 changed files with 69 additions and 50 deletions.
10 changes: 4 additions & 6 deletions chroma_api/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def is_valid(self, bundle, request=None):
if not len(private_key.strip()):
errors["private_key"].append(self.mandatory_message)
except KeyError:
# What? Now auth_type? assume existing key default case.
# What? Now auth_type? Assume existing key default case.
pass

return errors
Expand Down Expand Up @@ -135,10 +135,8 @@ class Meta:


class ClientMountResource(ChromaModelResource):
# This resource is only used for integration testing.

host = fields.ToOneField("chroma_api.host.HostResource", "host")
filesystem = fields.ToOneField("chroma_api.filesystem.FilesystemResource", "filesystem")
filesystem = fields.CharField()
mountpoint = fields.CharField()

class Meta:
Expand All @@ -157,7 +155,7 @@ def prepare_mount(self, client_mount):
def obj_create(self, bundle, **kwargs):
request = bundle.request
host = self.fields["host"].hydrate(bundle).obj
filesystem = self.fields["filesystem"].hydrate(bundle).obj
filesystem = ManagedFilesystem.objects.get(name=bundle.data["filesystem"])
mountpoint = bundle.data["mountpoint"]

client_mount = JobSchedulerClient.create_client_mount(host, filesystem, mountpoint)
Expand Down Expand Up @@ -209,7 +207,7 @@ def dehydrate_client_mounts(self, bundle):
search = lambda cm: cm.host == bundle.obj
mounts = ObjectCache.get(LustreClientMount, search)
return [
{"filesystem_name": mount.filesystem.name, "mountpoint": mount.mountpoint, "state": mount.state}
{"filesystem_name": mount.filesystem, "mountpoint": mount.mountpoint, "state": mount.state}
for mount in mounts
]

Expand Down
4 changes: 2 additions & 2 deletions chroma_core/lib/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ def host_client_mounts(cls, host_id):
return cls.get(LustreClientMount, lambda hcm: hcm.host_id == host_id)

@classmethod
def filesystem_client_mounts(cls, fs_id):
def filesystem_client_mounts(cls, fs_name):
from chroma_core.models.client_mount import LustreClientMount

return cls.get(LustreClientMount, lambda fcm: fcm.filesystem_id == fs_id)
return cls.get(LustreClientMount, lambda lcm: lcm.filesystem == fs_name)

@classmethod
def client_mount_copytools(cls, cm_id):
Expand Down
29 changes: 29 additions & 0 deletions chroma_core/migrations/0020_clientmounts_remove_fk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models


def resolve_fk(apps, schema_editor):
ClientMount = apps.get_model("chroma_core", "LustreClientMount")
ManagedFilesystem = apps.get_model("chroma_core", "ManagedFilesystem")
for m in ClientMount.objects.all():
m.filesystem = ManagedFilesystem.objects.get(id=m.filesystem).name
m.save()


class Migration(migrations.Migration):

dependencies = [
("chroma_core", "0019_auto_20200529_2026"),
]

operations = [
migrations.AlterField(
model_name="lustreclientmount",
name="filesystem",
field=models.CharField(help_text=b"Mounted filesystem", max_length=8),
),
migrations.RunPython(resolve_fk),
migrations.RemoveField(model_name="managedhost", name="client_filesystems",),
]
19 changes: 10 additions & 9 deletions chroma_core/models/client_mount.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from chroma_core.lib.cache import ObjectCache
from chroma_core.models.utils import CHARFIELD_MAX_LENGTH
from chroma_core.models.host import ManagedHost, HostOfflineAlert, HostContactAlert
from chroma_core.models.filesystem import ManagedFilesystem
from chroma_core.models.jobs import DeletableStatefulObject
from chroma_core.models.jobs import StateChangeJob
from chroma_core.models.alert import AlertState
Expand All @@ -18,7 +19,7 @@

class LustreClientMount(DeletableStatefulObject):
host = models.ForeignKey("ManagedHost", help_text="Mount host", related_name="client_mounts", on_delete=CASCADE)
filesystem = models.ForeignKey("ManagedFilesystem", help_text="Mounted filesystem", on_delete=CASCADE)
filesystem = models.CharField(max_length=8, help_text="Mounted filesystem", null=False, blank=False,)
mountpoint = models.CharField(
max_length=CHARFIELD_MAX_LENGTH, help_text="Filesystem mountpoint on host", null=True, blank=True
)
Expand Down Expand Up @@ -47,8 +48,10 @@ def get_deps(self, state=None):
deps.append(DependOn(self.host.lnet_configuration, "lnet_up", fix_state="unmounted"))

if state != "removed":
fs = ObjectCache.get_one(ManagedFilesystem, lambda mf: mf.name == self.filesystem)

# Depend on the fs being available.
deps.append(DependOn(self.filesystem, "available", fix_state="unmounted"))
deps.append(DependOn(fs, "available", fix_state="unmounted"))

# But if either the host or the filesystem are removed, the
# mount should follow.
Expand All @@ -62,9 +65,9 @@ def get_deps(self, state=None):
)
deps.append(
DependOn(
self.filesystem,
fs,
"available",
acceptable_states=list(set(self.filesystem.states) - set(["removed", "forgotten"])),
acceptable_states=list(set(fs.states) - set(["removed", "forgotten"])),
fix_state="removed",
)
)
Expand All @@ -74,7 +77,7 @@ def get_deps(self, state=None):
reverse_deps = {
"ManagedHost": lambda mh: ObjectCache.host_client_mounts(mh.id),
"LNetConfiguration": lambda lc: ObjectCache.host_client_mounts(lc.host.id),
"ManagedFilesystem": lambda mf: ObjectCache.filesystem_client_mounts(mf.id),
"ManagedFilesystem": lambda mf: ObjectCache.filesystem_client_mounts(mf.name),
}

class Meta:
Expand Down Expand Up @@ -150,9 +153,8 @@ def description(self):

def get_steps(self):
host = ObjectCache.get_one(ManagedHost, lambda mh: mh.id == self.lustre_client_mount.host_id)
from chroma_core.models.filesystem import ManagedFilesystem

filesystem = ObjectCache.get_one(ManagedFilesystem, lambda mf: mf.id == self.lustre_client_mount.filesystem_id)
filesystem = ObjectCache.get_one(ManagedFilesystem, lambda mf: mf.name == self.lustre_client_mount.filesystem)
args = dict(host=host, filesystems=[(filesystem.mount_path(), self.lustre_client_mount.mountpoint)])
return [(MountLustreFilesystemsStep, args)]

Expand Down Expand Up @@ -193,9 +195,8 @@ def description(self):

def get_steps(self):
host = ObjectCache.get_one(ManagedHost, lambda mh: mh.id == self.lustre_client_mount.host_id)
from chroma_core.models.filesystem import ManagedFilesystem

filesystem = ObjectCache.get_one(ManagedFilesystem, lambda mf: mf.id == self.lustre_client_mount.filesystem_id)
filesystem = ObjectCache.get_one(ManagedFilesystem, lambda mf: mf.name == self.lustre_client_mount.filesystem)
args = dict(host=host, filesystems=[(filesystem.mount_path(), self.lustre_client_mount.mountpoint)])
return [(UnmountLustreFilesystemsStep, args)]

Expand Down
7 changes: 0 additions & 7 deletions chroma_core/models/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,6 @@ class ManagedHost(DeletableStatefulObject):
default=False, help_text="True if there are package updates available for this server"
)

client_filesystems = models.ManyToManyField(
"ManagedFilesystem",
related_name="workers",
through="LustreClientMount",
help_text="Filesystems for which this node is a non-server worker",
)

corosync_ring0 = models.CharField(
max_length=255, help_text="Unicode string, hostname used to configure corosync ring0"
)
Expand Down
7 changes: 4 additions & 3 deletions chroma_core/models/stratagem.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,16 @@
from chroma_core.models import StateChangeJob, StateLock, StepResult, LustreClientMount
from chroma_help.help import help_text
from chroma_core.models import (
AlertStateBase,
AlertEvent,
AlertStateBase,
ManagedFilesystem,
ManagedHost,
ManagedMdt,
ManagedTarget,
ManagedTargetMount,
StorageResourceRecord,
Volume,
VolumeNode,
StorageResourceRecord,
)


Expand Down Expand Up @@ -678,7 +679,7 @@ def get_steps(self):
client_host = ManagedHost.objects.get(
Q(server_profile_id="stratagem_client") | Q(server_profile_id="stratagem_existing_client")
)
client_mount = LustreClientMount.objects.get(host_id=client_host.id, filesystem_id=self.filesystem.id)
client_mount = LustreClientMount.objects.get(host_id=client_host.id, filesystem=filesystem.name)

return [
(
Expand Down
21 changes: 10 additions & 11 deletions chroma_core/services/job_scheduler/job_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1106,25 +1106,22 @@ def key(td):
# if we have an entry with 'root'=true then move it to the front of the list before returning the result
return sorted(sorted_list, key=lambda entry: entry.get("root", False), reverse=True)

def create_client_mount(self, host_id, filesystem_id, mountpoint):
def create_client_mount(self, host_id, filesystem_name, mountpoint):
# RPC-callable
host = ObjectCache.get_one(ManagedHost, lambda mh: mh.id == host_id)
filesystem = ObjectCache.get_one(ManagedFilesystem, lambda mf: mf.id == filesystem_id)

mount = self._create_client_mount(host, filesystem, mountpoint)

mount = self._create_client_mount(host, filesystem_name, mountpoint)
self.progress.advance()
return mount.id

def _create_client_mount(self, host, filesystem, mountpoint):
def _create_client_mount(self, host, filesystem_name, mountpoint):
# Used for intra-JobScheduler calls
log.debug("Creating client mount for %s as %s:%s" % (filesystem, host, mountpoint))
log.debug("Creating client mount for %s as %s:%s" % (filesystem_name, host, mountpoint))

with self._lock:
from django.db import transaction

with transaction.atomic():
mount, created = LustreClientMount.objects.get_or_create(host=host, filesystem=filesystem)
mount, created = LustreClientMount.objects.get_or_create(host=host, filesystem=filesystem_name)
mount.mountpoint = mountpoint
mount.save()

Expand Down Expand Up @@ -1886,18 +1883,20 @@ def run_stratagem(self, mdts, fs_id, stratagem_data):
client_host = ManagedHost.objects.get(
Q(server_profile_id="stratagem_client") | Q(server_profile_id="stratagem_existing_client")
)
client_mount_exists = LustreClientMount.objects.filter(host_id=client_host.id, filesystem_id=fs_id).exists()
client_mount_exists = LustreClientMount.objects.filter(
host_id=client_host.id, filesystem=filesystem.name
).exists()

mountpoint = "/mnt/{}".format(filesystem.name)
if not client_mount_exists:
self._create_client_mount(client_host, filesystem, mountpoint)

client_mount = ObjectCache.get_one(
LustreClientMount, lambda mnt: mnt.host_id == client_host.id and mnt.filesystem_id == fs_id
LustreClientMount, lambda mnt: mnt.host_id == client_host.id and mnt.filesystem == filesystem.name
)
client_mount.state = "unmounted"
client_mount.mountpoint = mountpoint
client_mount.filesystem_id = filesystem.id
client_mount.filesystem = filesystem.name
client_mount.save()
ObjectCache.update(client_mount)

Expand Down
4 changes: 2 additions & 2 deletions chroma_core/services/job_scheduler/job_scheduler_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,10 @@ def create_targets(cls, targets_data):
return (list(ManagedTarget.objects.filter(id__in=target_ids)), Command.objects.get(pk=command_id))

@classmethod
def create_client_mount(cls, host, filesystem, mountpoint):
def create_client_mount(cls, host, filesystem_name, mountpoint):
from chroma_core.models import LustreClientMount

client_mount_id = JobSchedulerRpc().create_client_mount(host.id, filesystem.id, mountpoint)
client_mount_id = JobSchedulerRpc().create_client_mount(host.id, filesystem_name, mountpoint)
return LustreClientMount.objects.get(id=client_mount_id)

@classmethod
Expand Down
9 changes: 4 additions & 5 deletions chroma_core/services/lustre_audit/update_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,32 +138,31 @@ def update_client_mounts(self):
if client_mounts is None:
return

expected_fs_mounts = LustreClientMount.objects.select_related("filesystem").filter(host=self.host)
expected_fs_mounts = LustreClientMount.objects.filter(host=self.host)
actual_fs_mounts = [m["mountspec"].split(":/")[1] for m in client_mounts]

# Don't bother with the rest if there's nothing to do.
if len(expected_fs_mounts) == 0 and len(actual_fs_mounts) == 0:
return

for expected_mount in expected_fs_mounts:
if expected_mount.active and expected_mount.filesystem.name not in actual_fs_mounts:
if expected_mount.active and expected_mount.filesystem not in actual_fs_mounts:
update = dict(state="unmounted", mountpoint=None)
job_scheduler_notify.notify(expected_mount, self.started_at, update)
log.info("updated mount %s on %s -> inactive" % (expected_mount.mountpoint, self.host))

for actual_mount in client_mounts:
fsname = actual_mount["mountspec"].split(":/")[1]
try:
mount = [m for m in expected_fs_mounts if m.filesystem.name == fsname][0]
mount = [m for m in expected_fs_mounts if m.filesystem == fsname][0]
log.debug("mount: %s" % mount)
if not mount.active:
update = dict(state="mounted", mountpoint=actual_mount["mountpoint"])
job_scheduler_notify.notify(mount, self.started_at, update)
log.info("updated mount %s on %s -> active" % (actual_mount["mountpoint"], self.host))
except IndexError:
log.info("creating new mount %s on %s" % (actual_mount["mountpoint"], self.host))
filesystem = ManagedFilesystem.objects.get(name=fsname)
JobSchedulerClient.create_client_mount(self.host, filesystem, actual_mount["mountpoint"])
JobSchedulerClient.create_client_mount(self.host, fsname, actual_mount["mountpoint"])

def update_target_mounts(self):
# If mounts is None then nothing changed since the last update and so we can just return.
Expand Down
2 changes: 1 addition & 1 deletion iml-orm/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ pub struct ChromaCoreLustreclientmount {
pub not_deleted: Option<bool>,
pub mountpoint: Option<String>,
pub content_type_id: Option<i32>,
pub filesystem_id: i32,
pub filesystem: String,
pub host_id: i32,
}

Expand Down
3 changes: 1 addition & 2 deletions iml-orm/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ table! {
not_deleted -> Nullable<Bool>,
mountpoint -> Nullable<Varchar>,
content_type_id -> Nullable<Int4>,
filesystem_id -> Int4,
filesystem -> Varchar,
host_id -> Int4,
}
}
Expand Down Expand Up @@ -1713,7 +1713,6 @@ joinable!(chroma_core_lnetconfiguration -> chroma_core_managedhost (host_id));
joinable!(chroma_core_lnetconfiguration -> django_content_type (content_type_id));
joinable!(chroma_core_loadlnetjob -> chroma_core_job (job_ptr_id));
joinable!(chroma_core_loadlnetjob -> chroma_core_lnetconfiguration (lnet_configuration_id));
joinable!(chroma_core_lustreclientmount -> chroma_core_managedfilesystem (filesystem_id));
joinable!(chroma_core_lustreclientmount -> chroma_core_managedhost (host_id));
joinable!(chroma_core_lustreclientmount -> django_content_type (content_type_id));
joinable!(chroma_core_makeavailablefilesystemunavailable -> chroma_core_job (job_ptr_id));
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/chroma_api/test_client_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def setUp(self):
@mock.patch("chroma_core.lib.job.Step.invoke_agent", new=mock.Mock(return_value=agent_result_ok))
@remove_host_resources_patch
def test_removed_host_deletes_mount(self):
mount = LustreClientMount.objects.create(host=self.host, filesystem=self.fs, mountpoint="/mnt/testfs")
mount = LustreClientMount.objects.create(host=self.host, filesystem=self.fs.name, mountpoint="/mnt/testfs")

# Make sure it was created and that we can see it via API
self.assertEqual(self.api_get("/api/client_mount/%s/" % mount.id)["id"], mount.id)
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/chroma_core/models/test_advertised_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def create_fake_filesystem_client(self, active=False):
ManagedMdt.create_for_volume(synthetic_volume_full(self.server).id, filesystem=fs)
ManagedOst.create_for_volume(synthetic_volume_full(self.server).id, filesystem=fs)
state = "mounted" if active else "unmounted"
self.mount = LustreClientMount.objects.create(host=self.worker, filesystem=fs, state=state)
self.mount = LustreClientMount.objects.create(host=self.worker, filesystem=fs.name, state=state)

ObjectCache.add(LustreClientMount, self.mount)

Expand Down

0 comments on commit f10ba6b

Please sign in to comment.