Make probability with which an action is executed configurable

The probabilities with which each action was executed in each test cycle
was hard coded. This made it hard to create different test scenarios.

Now the probabilities can be defined in the config file. By setting them
to 0 or 1, they can be configured to never or to always run,
respectively. Note, that some actions depend on other actions to create
the state required for themselves.

Change-Id: I23b436cda01706b71fa309d4309f133b9181cc92
diff --git a/.pylintrc b/.pylintrc
index cde3b68..25948a5 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,5 +1,5 @@
 [MESSAGES CONTROL]
-disable=C0111, R0903, R0913, C0330
+disable=C0111, R0903, R0913, C0330, R0902
 
 [FORMAT]
 good-names=i,f
diff --git a/README.md b/README.md
index d74459b..d057f14 100644
--- a/README.md
+++ b/README.md
@@ -44,12 +44,28 @@
 
 The single configuration values are listed here:
 
-| key                | description                         | default value           |
-|--------------------|-------------------------------------|-------------------------|
-| `gerrit.url`       | URL of the Gerrit test server       | `http://localhost:8080` |
-| `gerrit.user`      | Gerrit user used for tests          | `admin`                 |
-| `gerrit.password`  | Password of Gerrit user             | `secret`                |
-| `testrun.duration` | Duration for which to run the tests | `null` (indefinitely)   |
+| key                | description                                                                           | default value           |
+|--------------------|---------------------------------------------------------------------------------------|-------------------------|
+| `gerrit.url`       | URL of the Gerrit test server                                                         | `http://localhost:8080` |
+| `gerrit.user`      | Gerrit user used for tests                                                            | `admin`                 |
+| `gerrit.password`  | Password of Gerrit user                                                               | `secret`                |
+| `testrun.duration` | Duration for which to run the tests                                                   | `null` (indefinitely)   |
+| `actions.*`        | Probability with which an action is performed in each cycle (`0`: never, `1`: always) | `1`                     |
+
+### Available actions
+
+The following actions can be performed by the tests:
+
+| key               | description                                                                              |
+|-------------------|------------------------------------------------------------------------------------------|
+| `clone_project`   | Test performs a clone of a project, that is assigned to the simulated user               |
+| `create_project`  | Test creates a new project via REST                                                      |
+| `fetch_project`   | Test fetches a project, that is assigned to the simulated user and was already cloned    |
+| `push_for_review` | Test creates random commits in a cloned project and pushes them to `refs/for/master`     |
+| `push_to_branch`  | Test creates random commits in a cloned project and pushes them to the remote's `master` |
+| `query_changes`   | Queries changes via REST                                                                 |
+| `query_projects`  | Queries projects via REST                                                                |
+| `review_change`   | Reviews a change via REST                                                                |
 
 ## Run
 
diff --git a/config.sample.yaml b/config.sample.yaml
index 57f4650..0e6f34b 100644
--- a/config.sample.yaml
+++ b/config.sample.yaml
@@ -5,3 +5,21 @@
 
 testrun:
   duration: null
+
+actions:
+  clone_project:
+    probability: 1
+  create_project:
+    probability: 1
+  fetch_project:
+    probability: 1
+  push_for_review:
+    probability: 1
+  push_to_branch:
+    probability: 1
+  query_changes:
+    probability: 1
+  query_projects:
+    probability: 1
+  review_change:
+    probability: 1
diff --git a/container/tools/actions/create_project.py b/container/tools/actions/create_project.py
index 91fd8dc..5c8eece 100644
--- a/container/tools/actions/create_project.py
+++ b/container/tools/actions/create_project.py
@@ -23,7 +23,7 @@
 
 
 class CreateProjectAction(abstract.AbstractAction):
-    def __init__(self, url, user, pwd, probability=0.00002):
+    def __init__(self, url, user, pwd, probability=1):
         super().__init__(url, user, pwd, probability)
         self.project_name = self._get_random_project_name()
 
diff --git a/container/tools/actions/fetch_project.py b/container/tools/actions/fetch_project.py
index 3030293..9d6e416 100644
--- a/container/tools/actions/fetch_project.py
+++ b/container/tools/actions/fetch_project.py
@@ -22,7 +22,7 @@
 
 
 class FetchProjectAction(abstract.AbstractAction):
-    def __init__(self, project_name, probability=0.1):
+    def __init__(self, project_name, probability=1):
         super().__init__(url=None, user=None, pwd=None, probability=probability)
         self.project_name = project_name
 
diff --git a/container/tools/actions/push_for_review.py b/container/tools/actions/push_for_review.py
index bda2d41..8e5d77a 100644
--- a/container/tools/actions/push_for_review.py
+++ b/container/tools/actions/push_for_review.py
@@ -16,7 +16,7 @@
 
 
 class PushForReviewAction(abstract_push.AbstractPushAction):
-    def __init__(self, project_name, probability=0.2):
+    def __init__(self, project_name, probability=1):
         super().__init__("HEAD:refs/for/master", project_name, probability=probability)
 
     def _prepare(self):
diff --git a/container/tools/actions/push_to_branch.py b/container/tools/actions/push_to_branch.py
index 9762461..996bc46 100644
--- a/container/tools/actions/push_to_branch.py
+++ b/container/tools/actions/push_to_branch.py
@@ -17,7 +17,7 @@
 
 
 class PushToBranchAction(abstract_push.AbstractPushAction):
-    def __init__(self, project_name, probability=0.2):
+    def __init__(self, project_name, probability=1):
         super().__init__("HEAD:master", project_name, probability=probability)
 
     def _prepare(self):
diff --git a/container/tools/actions/query_change_files.py b/container/tools/actions/query_change_files.py
index c011401..f5c85ce 100644
--- a/container/tools/actions/query_change_files.py
+++ b/container/tools/actions/query_change_files.py
@@ -20,7 +20,7 @@
 
 
 class QueryChangeFilesAction(abstract.AbstractAction):
-    def __init__(self, change_id, url, user, pwd, probability=0.2):
+    def __init__(self, change_id, url, user, pwd, probability=1):
         super().__init__(url, user, pwd, probability)
         self.change_id = change_id
         self.revision_id = 1
diff --git a/container/tools/actions/query_changes.py b/container/tools/actions/query_changes.py
index a465021..f238365 100644
--- a/container/tools/actions/query_changes.py
+++ b/container/tools/actions/query_changes.py
@@ -21,9 +21,6 @@
 
 
 class QueryChangesAction(abstract.AbstractAction):
-    def __init__(self, url, user, pwd, probability=0.2):
-        super().__init__(url, user, pwd, probability)
-
     def _execute_action(self):
         rest_url = self._assemble_url()
         response = requests.get(rest_url, auth=(self.user, self.pwd))
diff --git a/container/tools/actions/query_projects.py b/container/tools/actions/query_projects.py
index 989f18d..5205a87 100644
--- a/container/tools/actions/query_projects.py
+++ b/container/tools/actions/query_projects.py
@@ -23,9 +23,6 @@
 
 
 class QueryProjectsAction(abstract.AbstractAction):
-    def __init__(self, url, user, pwd, probability=0.05):
-        super().__init__(url, user, pwd, probability)
-
     def _execute_action(self):
         selected_project = None
         rest_url = self._assemble_url()
diff --git a/container/tools/actions/review_change.py b/container/tools/actions/review_change.py
index 20db3bd..88df4a9 100644
--- a/container/tools/actions/review_change.py
+++ b/container/tools/actions/review_change.py
@@ -22,7 +22,7 @@
 
 # pylint: disable=W0703
 class ReviewChangeAction(abstract.AbstractAction):
-    def __init__(self, url, user, pwd, probability=0.3):
+    def __init__(self, url, user, pwd, probability=1):
         super().__init__(url, user, pwd, probability)
         self.change_id = self._get_change_id()
         self.revision_id = 1
diff --git a/container/tools/config/parser.py b/container/tools/config/parser.py
index 2e2466a..f370d23 100644
--- a/container/tools/config/parser.py
+++ b/container/tools/config/parser.py
@@ -19,6 +19,16 @@
 DEFAULTS = {
     "gerrit": {"url": None, "user": "admin", "password": "secret"},
     "testrun": {"duration": None},
+    "actions": {
+        "clone_project": {"probability": 1},
+        "create_project": {"probability": 1},
+        "fetch_project": {"probability": 1},
+        "push_for_review": {"probability": 1},
+        "push_to_branch": {"probability": 1},
+        "query_changes": {"probability": 1},
+        "query_projects": {"probability": 1},
+        "review_change": {"probability": 1},
+    },
 }
 
 ARG_TO_CONFIG_MAPPING = {
diff --git a/container/tools/start_test.py b/container/tools/start_test.py
index e504586..39ac5f6 100755
--- a/container/tools/start_test.py
+++ b/container/tools/start_test.py
@@ -40,6 +40,8 @@
             else None
         )
 
+        self.action_config = test_config["actions"]
+
         self.owned_projects = set()
         self.cloned_projects = set()
 
@@ -88,13 +90,23 @@
         return np.random.choice(input_list, 1, p=probabilities).tolist()[0]
 
     def _exec_create_project_action(self):
-        action = actions.CreateProjectAction(self.url, self.user, self.pwd)
+        action = actions.CreateProjectAction(
+            self.url,
+            self.user,
+            self.pwd,
+            self.action_config["create_project"]["probability"],
+        )
         project_name = action.execute()
         if not action.failed and project_name:
             self.owned_projects.add(project_name)
 
     def _exec_list_projects_action(self):
-        action = actions.QueryProjectsAction(self.url, self.user, self.pwd)
+        action = actions.QueryProjectsAction(
+            self.url,
+            self.user,
+            self.pwd,
+            self.action_config["query_projects"]["probability"],
+        )
         project_name = action.execute()
         if not action.failed and project_name:
             self.owned_projects.add(project_name)
@@ -105,6 +117,7 @@
             self.user,
             self.pwd,
             self._choose_from_list_poisson(list(self.owned_projects)),
+            self.action_config["clone_project"]["probability"],
         )
         action.execute()
         if not action.failed and action.was_executed:
@@ -112,28 +125,41 @@
 
     def _exec_fetch_project_action(self):
         action = actions.FetchProjectAction(
-            self._choose_from_list_poisson(list(self.cloned_projects))
+            self._choose_from_list_poisson(list(self.cloned_projects)),
+            self.action_config["fetch_project"]["probability"],
         )
         action.execute()
 
     def _exec_push_commit_action(self):
         action = actions.PushToBranchAction(
-            self._choose_from_list_poisson(list(self.cloned_projects))
+            self._choose_from_list_poisson(list(self.cloned_projects)),
+            self.action_config["push_to_branch"]["probability"],
         )
         action.execute()
 
     def _exec_push_change_action(self):
         action = actions.PushForReviewAction(
-            self._choose_from_list_poisson(list(self.cloned_projects))
+            self._choose_from_list_poisson(list(self.cloned_projects)),
+            self.action_config["push_for_review"]["probability"],
         )
         action.execute()
 
     def _exec_query_changes_action(self):
-        action = actions.QueryChangesAction(self.url, self.user, self.pwd)
+        action = actions.QueryChangesAction(
+            self.url,
+            self.user,
+            self.pwd,
+            self.action_config["query_changes"]["probability"],
+        )
         action.execute()
 
     def _exec_review_change_action(self):
-        action = actions.ReviewChangeAction(self.url, self.user, self.pwd)
+        action = actions.ReviewChangeAction(
+            self.url,
+            self.user,
+            self.pwd,
+            self.action_config["review_change"]["probability"],
+        )
         action.execute()