Tweak how files and hosts are created to ensure idempotency
Files and hosts can be created from different places and as such, we need to double check if they have not already been created whenever we might want to create them. It should not be possible to have the same file or the same host more than once in a given playbook due to unique constraints. Change-Id: I6023949f4ad026086ba690789ddeebf5bb1d0a0d Depends-On: https://review.openstack.org/#/c/625948/ Depends-On: https://review.openstack.org/#/c/625961/
This commit is contained in:
parent
188eba9b0d
commit
014d65a5fa
|
@ -150,15 +150,11 @@ class CallbackModule(CallbackBase):
|
|||
|
||||
# Create the playbook
|
||||
self.playbook = self.client.post(
|
||||
"/api/v1/playbooks",
|
||||
ansible_version=ansible_version,
|
||||
arguments=arguments,
|
||||
status="running",
|
||||
file=dict(path=path, content=self._read_file(path)),
|
||||
"/api/v1/playbooks", ansible_version=ansible_version, arguments=arguments, status="running", path=path
|
||||
)
|
||||
|
||||
# Record all the files involved in the playbook
|
||||
self._load_files(playbook._loader._FILE_CACHE.keys())
|
||||
# Record the playbook file
|
||||
self._get_or_create_file(path)
|
||||
|
||||
return self.playbook
|
||||
|
||||
|
@ -193,16 +189,8 @@ class CallbackModule(CallbackBase):
|
|||
path = self.playbook["path"]
|
||||
lineno = 1
|
||||
|
||||
# Ensure this task's file was added to the playbook -- files that are
|
||||
# dynamically included do not show up in the playbook or play context
|
||||
self._load_files([path])
|
||||
|
||||
# Find the task file (is there a better way?)
|
||||
task_file = self.playbook["file"]["id"]
|
||||
for file in self.playbook["files"]:
|
||||
if file["path"] == path:
|
||||
task_file = file["id"]
|
||||
break
|
||||
# Get task file
|
||||
task_file = self._get_or_create_file(path)
|
||||
|
||||
self.task = self.client.post(
|
||||
"/api/v1/tasks",
|
||||
|
@ -211,7 +199,7 @@ class CallbackModule(CallbackBase):
|
|||
action=task.action,
|
||||
play=self.play["id"],
|
||||
playbook=self.playbook["id"],
|
||||
file=task_file,
|
||||
file=task_file["id"],
|
||||
tags=task._attributes["tags"],
|
||||
lineno=lineno,
|
||||
handler=handler,
|
||||
|
@ -264,28 +252,46 @@ class CallbackModule(CallbackBase):
|
|||
"/api/v1/playbooks/%s" % self.playbook["id"], status=status, ended=datetime.datetime.now().isoformat()
|
||||
)
|
||||
|
||||
def _get_one_item(self, endpoint, **query):
|
||||
"""
|
||||
Searching with the API returns a list of results. This method is used
|
||||
when our expectation is that we would only ever get back one result back
|
||||
due to unique database model constraints.
|
||||
"""
|
||||
match = self.client.get(endpoint, **query)
|
||||
if match["count"] > 1:
|
||||
error = "Received more than one result for %s with %s" % (endpoint, str(query))
|
||||
self.log.error(error)
|
||||
raise Exception(error)
|
||||
elif match["count"] == 0:
|
||||
return False
|
||||
else:
|
||||
return match["results"][0]
|
||||
|
||||
def _get_or_create_file(self, file_):
|
||||
self.log.debug("Getting or creating file: %s" % file_)
|
||||
query = dict(playbook=self.playbook["id"], path=file_)
|
||||
playbook_file = self._get_one_item("/api/v1/files", **query)
|
||||
if not playbook_file:
|
||||
playbook_file = self.client.post(
|
||||
"/api/v1/files", playbook=self.playbook["id"], path=file_, content=self._read_file(file_)
|
||||
)
|
||||
|
||||
return playbook_file
|
||||
|
||||
def _load_files(self, files):
|
||||
self.log.debug("Loading %s file(s)..." % len(files))
|
||||
playbook_files = [file["path"] for file in self.playbook["files"]]
|
||||
for file in files:
|
||||
if file not in playbook_files:
|
||||
self.client.post(
|
||||
"/api/v1/playbooks/%s/files" % self.playbook["id"], path=file, content=self._read_file(file)
|
||||
)
|
||||
for file_ in files:
|
||||
self._get_or_create_file(file_)
|
||||
|
||||
def _get_or_create_host(self, host):
|
||||
self.log.debug("Getting or creating host: %s" % host)
|
||||
# Don't query the API if we already have this host
|
||||
for playbook_host in self.playbook["hosts"]:
|
||||
if host == playbook_host["name"]:
|
||||
return playbook_host
|
||||
|
||||
query = dict(playbook=self.playbook["id"], name=host)
|
||||
playbook_host = self._get_one_item("/api/v1/hosts", **query)
|
||||
if not playbook_host:
|
||||
# TODO: Implement logic for computing the host alias
|
||||
playbook_host = self.client.post("/api/v1/hosts", name=host, alias=host, playbook=self.playbook["id"])
|
||||
|
||||
# Refresh cached playbook
|
||||
self.playbook = self.client.get("/api/v1/playbooks/%s" % self.playbook["id"])
|
||||
|
||||
return playbook_host
|
||||
|
||||
def _load_hosts(self, hosts):
|
||||
|
|
Loading…
Reference in New Issue