From cf541956e87493a103a8cd42bbff26e9aadfcbd4 Mon Sep 17 00:00:00 2001 From: venkata anil Date: Mon, 9 Aug 2021 22:29:14 +0530 Subject: [PATCH] implement locking mechanism in rally Rally is not having locking mechanism to control rally processes simultaneous access to the same OSP resource. For example, we don't want two different rally processes to delete the same floating ip. In this implementation we create a "RallyLock" table in rally DB which can store a unique UUID. When a user wants only one process to modify a OSP resource at a time, take a lock by inserting resource's (for example floating ip) uuid into this table. If another process wants to take lock, it will get error as table won't allow duplicate values for uuid. Sqlalchemy is throwing errors when this DB code is implemented as a rally plugin. So we needed to find the alternate option i.e python package to make this DB changes working. To acquire and release a lock, rally scenario has to call the api like from browbeat_rally.db import api as db_api from oslo_db import exception as db_exc net_id = net['network']['id'] try: db_api.acquire_lock(net_id) except db_exc.DBDuplicateEntry: LOG.info("This uuid {} is already in use".format(net_id)) db_api.release_lock(net_id) Change-Id: I0c14a3c0ddb3e2d4e3478e763d2ee4066440b9ac --- .../browbeat-rally/browbeat_rally/__init__.py | 0 .../browbeat_rally/db/__init__.py | 0 .../browbeat-rally/browbeat_rally/db/api.py | 32 +++++++++++++ .../browbeat_rally/db/models.py | 23 ++++++++++ .../browbeat_rally/db/schema.py | 46 +++++++++++++++++++ .../roles/rally/files/browbeat-rally/setup.py | 18 ++++++++ .../roles/rally/files/create_lock_table.py | 16 +++++++ ansible/install/roles/rally/tasks/main.yml | 19 ++++++++ 8 files changed, 154 insertions(+) create mode 100644 ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/__init__.py create mode 100644 ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/__init__.py create mode 100644 ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/api.py create mode 100644 ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/models.py create mode 100644 ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/schema.py create mode 100644 ansible/install/roles/rally/files/browbeat-rally/setup.py create mode 100644 ansible/install/roles/rally/files/create_lock_table.py diff --git a/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/__init__.py b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/__init__.py b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/api.py b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/api.py new file mode 100644 index 000000000..edc7626ac --- /dev/null +++ b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/api.py @@ -0,0 +1,32 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from rally.common import db + +from browbeat_rally.db import models + + +@db.with_session +def get_lock(session, lock_uuid): + lock = models.RallyLock(lock_uuid=lock_uuid) + session.add(lock) + session.commit() + lock = lock.as_dict() + return lock + + +@db.with_session +def lock_list(session): + locks = [] + query = session.query(models.RallyLock) + for lock in query.all(): + locks.append(lock.as_dict()) + return locks diff --git a/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/models.py b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/models.py new file mode 100644 index 000000000..864fa86ab --- /dev/null +++ b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/models.py @@ -0,0 +1,23 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import sqlalchemy as sa + +from rally.common.db import models + + +BASE = models.BASE + + +class RallyLock(BASE, models.RallyBase): + __tablename__ = "rallylocks" + + lock_uuid = sa.Column(sa.String(36), primary_key=True, nullable=False) diff --git a/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/schema.py b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/schema.py new file mode 100644 index 000000000..f619a3ac6 --- /dev/null +++ b/ansible/install/roles/rally/files/browbeat-rally/browbeat_rally/db/schema.py @@ -0,0 +1,46 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import tempfile +from sqlalchemy import schema +from oslo_db import options as db_options +from browbeat_rally.db import models +from rally.common import cfg +from oslo_db.sqlalchemy import session as db_session + + +CONF = cfg.CONF + +# rally.conf always uses stack.sqlite database +db_options.set_defaults( + CONF, connection="sqlite:///%s/stack.sqlite" % tempfile.gettempdir()) + + +def _create_facade_lazily(): + return db_session.EngineFacade.from_config(CONF) + + +def get_engine(): + facade = _create_facade_lazily() + return facade.get_engine() + + +# alternate way to create engine is +# from oslo_db.sqlalchemy import engines +# engine = engines.create_engine("sqlite:///%s/stack.sqlite" % tempfile.gettempdir()) +def schema_create(): + engine = get_engine() + metadata = schema.MetaData() + table = schema.Table("rallylocks", metadata) + # delete rallylocks table + models.BASE.metadata.drop_all(engine, [table], checkfirst=True) + # recreate rallylocks table + models.BASE.metadata.create_all(engine) diff --git a/ansible/install/roles/rally/files/browbeat-rally/setup.py b/ansible/install/roles/rally/files/browbeat-rally/setup.py new file mode 100644 index 000000000..38c7b7387 --- /dev/null +++ b/ansible/install/roles/rally/files/browbeat-rally/setup.py @@ -0,0 +1,18 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from setuptools import setup, find_packages + +setup( + name='browbeat_rally', + version='0.0.1', + packages=find_packages(),) diff --git a/ansible/install/roles/rally/files/create_lock_table.py b/ansible/install/roles/rally/files/create_lock_table.py new file mode 100644 index 000000000..e21469680 --- /dev/null +++ b/ansible/install/roles/rally/files/create_lock_table.py @@ -0,0 +1,16 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from browbeat_rally.db import schema + + +if __name__ == "__main__": + schema.schema_create() diff --git a/ansible/install/roles/rally/tasks/main.yml b/ansible/install/roles/rally/tasks/main.yml index c49530e14..400832d71 100644 --- a/ansible/install/roles/rally/tasks/main.yml +++ b/ansible/install/roles/rally/tasks/main.yml @@ -56,6 +56,25 @@ - name: Setup rally database shell: . {{ rally_venv }}/bin/activate; rally db recreate +# Install rally extra code which we develop in browbeat +# as python package. This code can be used in browbeat rally plugins. +# Sqlalchemy giving errors when we run this extra DB code as rally plugin +# so we came up with this python package approach. +- name: install rally browbeat code + shell: | + source {{ rally_venv }}/bin/activate + pip install . + args: + executable: /bin/bash + chdir: "{{ role_path }}/files/browbeat-rally" + when: rhosp_version is version('15.0', '>=') + +- name: create lock table in rally db for dynamic workloads + shell: | + source {{ rally_venv }}/bin/activate + python {{ role_path }}/files/create_lock_table.py + when: rhosp_version is version('15.0', '>=') + - block: - name: copy of stackrc copy: