diff --git a/congressclient/osc/v1/policy.py b/congressclient/osc/v1/policy.py index 620c5fd..ed99dd2 100644 --- a/congressclient/osc/v1/policy.py +++ b/congressclient/osc/v1/policy.py @@ -23,6 +23,7 @@ from keystoneauth1 import exceptions from oslo_log import log as logging from oslo_serialization import jsonutils import six +import yaml from congressclient.common import utils @@ -293,6 +294,44 @@ class CreatePolicy(show.ShowOne): return zip(*sorted(six.iteritems(data))) +class CreatePolicyFromFile(show.ShowOne): + """Create a policy.""" + + log = logging.getLogger(__name__ + '.CreatePolicy') + + def get_parser(self, prog_name): + parser = super(CreatePolicyFromFile, self).get_parser(prog_name) + parser.add_argument( + 'policy_file_path', + metavar="", + help="Path to policy file") + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + client = self.app.client_manager.congressclient + with open(parsed_args.policy_file_path, "r") as stream: + policies = yaml.load_all(stream) + try: + body = next(policies) + except StopIteration: + raise Exception('No policy found in file.') + try: + body = next(policies) + raise Exception( + 'More than one policy found in file. None imported.') + except StopIteration: + pass + data = client.create_policy(body) + + def rule_dict_to_string(rules): + rule_str_list = [rule['rule'] for rule in rules] + return "\n".join(rule_str_list) + + data['rules'] = rule_dict_to_string(data['rules']) + return zip(*sorted(six.iteritems(data))) + + class DeletePolicy(command.Command): """Delete a policy.""" diff --git a/congressclient/tests/v1/test_policy.py b/congressclient/tests/v1/test_policy.py index 477d20c..324596c 100644 --- a/congressclient/tests/v1/test_policy.py +++ b/congressclient/tests/v1/test_policy.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. # +import os import mock @@ -47,6 +48,43 @@ class TestCreatePolicy(common.TestCongressBase): self.assertEqual(filtered, result) +class TestCreatePolicyFromFile(common.TestCongressBase): + + def test_create_policy(self): + policy_path = os.path.dirname( + os.path.abspath(__file__)) + '/test_policy_file.yaml' + policy_id = "e531f2b3-3d97-42c0-b3b5-b7b6ab532018" + response = {"description": "", + "id": policy_id, + "name": "test_policy", + "kind": "nonrecursive", + "owner": "system", + "abbreviation": "abbr", + "rules": [ + {'comment': 'test comment', 'name': 'test name', + 'rule': 'p(x) :- q(x)'}, + {'comment': 'test comment2', 'name': 'test name2', + 'rule': 'p(x) :- q2(x)'}]} + + arglist = [policy_path] + verifylist = [ + ('policy_file_path', policy_path), + ] + + mocker = mock.Mock(return_value=response) + self.app.client_manager.congressclient.create_policy = mocker + cmd = policy.CreatePolicyFromFile(self.app, self.namespace) + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = list(cmd.take_action(parsed_args)) + filtered = [('abbreviation', 'description', 'id', 'kind', 'name', + 'owner', 'rules'), + ('abbr', '', policy_id, 'nonrecursive', + 'test_policy', 'system', + 'p(x) :- q(x)\n' + 'p(x) :- q2(x)')] + self.assertEqual(filtered, result) + + class TestShowPolicy(common.TestCongressBase): def test_show_policy(self): policy_id = "14f2897a-155a-4c9d-b3de-ef85c0a171d8" diff --git a/congressclient/tests/v1/test_policy_file.yaml b/congressclient/tests/v1/test_policy_file.yaml new file mode 100644 index 0000000..8b96875 --- /dev/null +++ b/congressclient/tests/v1/test_policy_file.yaml @@ -0,0 +1,17 @@ +--- +name: PauseBadFlavors +description: "Pause any server using a flavor that is not permitted" +rules: + - + comment: "User should customize this. Permitted flavors." + rule: permitted_flavor('m1.tiny') + - + comment: "User should customize this. Permitted flavors." + rule: permitted_flavor('m1.large') + - + rule: > + server_with_bad_flavor(id) :- nova:servers(id=id,flavor_id=flavor_id), + nova:flavors(id=flavor_id, name=flavor), not permitted_flavor(flavor) + - + comment: "Remediation: Pause any VM that shows up in the server_with_bad_flavor table" + rule: "execute[nova:servers.pause(id)] :- server_with_bad_flavor(id), nova:servers(id,status='ACTIVE')" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 4e9d140..20a94c4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,6 +28,7 @@ openstack.cli.extension = openstack.congressclient.v1 = congress_policy_create = congressclient.osc.v1.policy:CreatePolicy + congress_policy_create-from-file = congressclient.osc.v1.policy:CreatePolicyFromFile congress_policy_delete = congressclient.osc.v1.policy:DeletePolicy congress_policy_show = congressclient.osc.v1.policy:ShowPolicy congress_policy_rule_create = congressclient.osc.v1.policy:CreatePolicyRule