Compare commits
14 Commits
stable/202
...
stable/202
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
456f42e1b9 | ||
|
|
b5b1bc5473 | ||
|
|
c049a533e3 | ||
|
|
dbc06d1504 | ||
|
|
54b3b58428 | ||
|
|
8b0f1dbf66 | ||
|
|
0af13220da | ||
|
|
f85521f3c6 | ||
|
|
238bb50f53 | ||
|
|
db85f32675 | ||
|
|
f6015fd625 | ||
|
|
a9dc3794a6 | ||
|
|
d6f169197e | ||
|
|
bc5922c684 |
@@ -2,4 +2,4 @@
|
|||||||
host=review.opendev.org
|
host=review.opendev.org
|
||||||
port=29418
|
port=29418
|
||||||
project=openstack/watcher.git
|
project=openstack/watcher.git
|
||||||
defaultbranch=stable/2024.1
|
defaultbranch=stable/2024.2
|
||||||
|
|||||||
@@ -275,6 +275,9 @@ function install_watcherclient {
|
|||||||
git_clone_by_name "python-watcherclient"
|
git_clone_by_name "python-watcherclient"
|
||||||
setup_dev_lib "python-watcherclient"
|
setup_dev_lib "python-watcherclient"
|
||||||
fi
|
fi
|
||||||
|
if [[ "$GLOBAL_VENV" == "True" ]]; then
|
||||||
|
sudo ln -sf /opt/stack/data/venv/bin/watcher /usr/local/bin
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# install_watcher() - Collect source and prepare
|
# install_watcher() - Collect source and prepare
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ then write_uwsgi_config "$WATCHER_UWSGI_CONF" "$WATCHER_UWSGI" "/infra-optim"
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Migrate the database
|
# Migrate the database
|
||||||
watcher-db-manage upgrade || die $LINO "DB migration error"
|
$WATCHER_BIN_DIR/watcher-db-manage upgrade || die $LINO "DB migration error"
|
||||||
|
|
||||||
start_watcher
|
start_watcher
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,16 @@
|
|||||||
# Andi Chandler <andi@gowling.com>, 2020. #zanata
|
# Andi Chandler <andi@gowling.com>, 2020. #zanata
|
||||||
# Andi Chandler <andi@gowling.com>, 2022. #zanata
|
# Andi Chandler <andi@gowling.com>, 2022. #zanata
|
||||||
# Andi Chandler <andi@gowling.com>, 2023. #zanata
|
# Andi Chandler <andi@gowling.com>, 2023. #zanata
|
||||||
|
# Andi Chandler <andi@gowling.com>, 2024. #zanata
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: python-watcher\n"
|
"Project-Id-Version: python-watcher\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-08-14 03:05+0000\n"
|
"POT-Creation-Date: 2024-05-31 14:40+0000\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"PO-Revision-Date: 2023-06-21 07:54+0000\n"
|
"PO-Revision-Date: 2024-04-18 12:21+0000\n"
|
||||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||||
"Language-Team: English (United Kingdom)\n"
|
"Language-Team: English (United Kingdom)\n"
|
||||||
"Language: en_GB\n"
|
"Language: en_GB\n"
|
||||||
@@ -63,6 +64,9 @@ msgstr "2.0.0"
|
|||||||
msgid "2023.1 Series Release Notes"
|
msgid "2023.1 Series Release Notes"
|
||||||
msgstr "2023.1 Series Release Notes"
|
msgstr "2023.1 Series Release Notes"
|
||||||
|
|
||||||
|
msgid "2023.2 Series Release Notes"
|
||||||
|
msgstr "2023.2 Series Release Notes"
|
||||||
|
|
||||||
msgid "3.0.0"
|
msgid "3.0.0"
|
||||||
msgstr "3.0.0"
|
msgstr "3.0.0"
|
||||||
|
|
||||||
|
|||||||
2
tox.ini
2
tox.ini
@@ -8,7 +8,7 @@ basepython = python3
|
|||||||
usedevelop = True
|
usedevelop = True
|
||||||
allowlist_externals = find
|
allowlist_externals = find
|
||||||
rm
|
rm
|
||||||
install_command = pip install -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2024.1} {opts} {packages}
|
install_command = pip install -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2024.2} {opts} {packages}
|
||||||
setenv =
|
setenv =
|
||||||
VIRTUAL_ENV={envdir}
|
VIRTUAL_ENV={envdir}
|
||||||
deps =
|
deps =
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def upgrade():
|
|||||||
|
|
||||||
op.create_table(
|
op.create_table(
|
||||||
'apscheduler_jobs',
|
'apscheduler_jobs',
|
||||||
sa.Column('id', sa.Unicode(191, _warn_on_bytestring=False),
|
sa.Column('id', sa.Unicode(191),
|
||||||
nullable=False),
|
nullable=False),
|
||||||
sa.Column('next_run_time', sa.Float(25), index=True),
|
sa.Column('next_run_time', sa.Float(25), index=True),
|
||||||
sa.Column('job_state', sa.LargeBinary, nullable=False),
|
sa.Column('job_state', sa.LargeBinary, nullable=False),
|
||||||
|
|||||||
@@ -228,7 +228,8 @@ class Connection(api.BaseConnection):
|
|||||||
for relationship in relationships:
|
for relationship in relationships:
|
||||||
if not relationship.uselist:
|
if not relationship.uselist:
|
||||||
# We have a One-to-X relationship
|
# We have a One-to-X relationship
|
||||||
query = query.options(joinedload(relationship.key))
|
query = query.options(joinedload(
|
||||||
|
getattr(model, relationship.key)))
|
||||||
return query
|
return query
|
||||||
|
|
||||||
@oslo_db_api.retry_on_deadlock
|
@oslo_db_api.retry_on_deadlock
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from apscheduler.jobstores.base import ConflictingIdError
|
|||||||
from apscheduler.jobstores import sqlalchemy
|
from apscheduler.jobstores import sqlalchemy
|
||||||
from apscheduler.util import datetime_to_utc_timestamp
|
from apscheduler.util import datetime_to_utc_timestamp
|
||||||
from apscheduler.util import maybe_ref
|
from apscheduler.util import maybe_ref
|
||||||
|
from apscheduler.util import utc_timestamp_to_datetime
|
||||||
|
|
||||||
from watcher.common import context
|
from watcher.common import context
|
||||||
from watcher.common import service
|
from watcher.common import service
|
||||||
@@ -32,7 +33,7 @@ try:
|
|||||||
except ImportError: # pragma: nocover
|
except ImportError: # pragma: nocover
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from sqlalchemy import Table, MetaData, select, and_
|
from sqlalchemy import Table, MetaData, select, and_, null
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
|
|
||||||
@@ -58,8 +59,7 @@ class WatcherJobStore(sqlalchemy.SQLAlchemyJobStore):
|
|||||||
super(WatcherJobStore, self).__init__(url, engine, tablename,
|
super(WatcherJobStore, self).__init__(url, engine, tablename,
|
||||||
metadata, pickle_protocol)
|
metadata, pickle_protocol)
|
||||||
metadata = maybe_ref(metadata) or MetaData()
|
metadata = maybe_ref(metadata) or MetaData()
|
||||||
self.jobs_t = Table(tablename, metadata, autoload=True,
|
self.jobs_t = Table(tablename, metadata, autoload_with=engine)
|
||||||
autoload_with=engine)
|
|
||||||
service_ident = service.ServiceHeartbeat.get_service_name()
|
service_ident = service.ServiceHeartbeat.get_service_name()
|
||||||
self.tag = tag or {'host': service_ident[0], 'name': service_ident[1]}
|
self.tag = tag or {'host': service_ident[0], 'name': service_ident[1]}
|
||||||
self.service_id = objects.Service.list(context=context.make_context(),
|
self.service_id = objects.Service.list(context=context.make_context(),
|
||||||
@@ -79,7 +79,8 @@ class WatcherJobStore(sqlalchemy.SQLAlchemyJobStore):
|
|||||||
'tag': jsonutils.dumps(self.tag)
|
'tag': jsonutils.dumps(self.tag)
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
self.engine.execute(insert)
|
with self.engine.begin() as conn:
|
||||||
|
conn.execute(insert)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
raise ConflictingIdError(job.id)
|
raise ConflictingIdError(job.id)
|
||||||
|
|
||||||
@@ -88,20 +89,36 @@ class WatcherJobStore(sqlalchemy.SQLAlchemyJobStore):
|
|||||||
self._fix_paused_jobs_sorting(jobs)
|
self._fix_paused_jobs_sorting(jobs)
|
||||||
return jobs
|
return jobs
|
||||||
|
|
||||||
|
def get_next_run_time(self):
|
||||||
|
selectable = select(self.jobs_t.c.next_run_time).\
|
||||||
|
where(self.jobs_t.c.next_run_time != null()).\
|
||||||
|
order_by(self.jobs_t.c.next_run_time).limit(1)
|
||||||
|
with self.engine.begin() as connection:
|
||||||
|
# NOTE(danms): The apscheduler implementation of this gets a
|
||||||
|
# decimal.Decimal back from scalar() which causes
|
||||||
|
# utc_timestamp_to_datetime() to choke since it is expecting a
|
||||||
|
# python float. Assume this is SQLAlchemy 2.0 stuff, so just
|
||||||
|
# coerce to a float here.
|
||||||
|
next_run_time = connection.execute(selectable).scalar()
|
||||||
|
return utc_timestamp_to_datetime(float(next_run_time)
|
||||||
|
if next_run_time is not None
|
||||||
|
else None)
|
||||||
|
|
||||||
def _get_jobs(self, *conditions):
|
def _get_jobs(self, *conditions):
|
||||||
jobs = []
|
jobs = []
|
||||||
conditions += (self.jobs_t.c.service_id == self.service_id,)
|
conditions += (self.jobs_t.c.service_id == self.service_id,)
|
||||||
selectable = select(
|
selectable = select(
|
||||||
[self.jobs_t.c.id, self.jobs_t.c.job_state, self.jobs_t.c.tag]
|
self.jobs_t.c.id, self.jobs_t.c.job_state, self.jobs_t.c.tag
|
||||||
).order_by(self.jobs_t.c.next_run_time).where(and_(*conditions))
|
).order_by(self.jobs_t.c.next_run_time).where(and_(*conditions))
|
||||||
failed_job_ids = set()
|
failed_job_ids = set()
|
||||||
for row in self.engine.execute(selectable):
|
with self.engine.begin() as conn:
|
||||||
try:
|
for row in conn.execute(selectable):
|
||||||
jobs.append(self._reconstitute_job(row.job_state))
|
try:
|
||||||
except Exception:
|
jobs.append(self._reconstitute_job(row.job_state))
|
||||||
self._logger.exception(
|
except Exception:
|
||||||
'Unable to restore job "%s" -- removing it', row.id)
|
self._logger.exception(
|
||||||
failed_job_ids.add(row.id)
|
'Unable to restore job "%s" -- removing it', row.id)
|
||||||
|
failed_job_ids.add(row.id)
|
||||||
|
|
||||||
# Remove all the jobs we failed to restore
|
# Remove all the jobs we failed to restore
|
||||||
if failed_job_ids:
|
if failed_job_ids:
|
||||||
|
|||||||
Reference in New Issue
Block a user