Blame SOURCES/bz1447910-01-bundle-resources-are-missing-meta-attributes.patch

f778fe
From db1a118ed0d36633c67513961b479f8fae3cc2b9 Mon Sep 17 00:00:00 2001
f778fe
From: Ivan Devat <idevat@redhat.com>
f778fe
Date: Thu, 15 Jun 2017 11:46:12 +0200
f778fe
Subject: [PATCH] squash bz1447910 bundle resources are missing meta
f778fe
f778fe
d21dd0e6b4d3 make resource enable | disable work with bundles
f778fe
f778fe
27d46c115210 make resource manage | unmanage work with bundles
f778fe
f778fe
c963cdcd321b show bundles' meta attributes in resources listing
f778fe
f778fe
f1923af76d73 support meta attributes in 'resource bundle create'
f778fe
f778fe
e09015ee868a support meta attributes in 'resource bundle update'
f778fe
f778fe
c6e70a38346a stop bundles when deleting them
f778fe
---
f778fe
 pcs/cli/resource/parse_args.py                     |   4 +-
f778fe
 pcs/cli/resource/test/test_parse_args.py           |  70 ++++++++
f778fe
 pcs/lib/cib/nvpair.py                              |  12 +-
f778fe
 pcs/lib/cib/resource/bundle.py                     |  17 +-
f778fe
 pcs/lib/cib/resource/common.py                     |  40 +++--
f778fe
 pcs/lib/cib/test/test_nvpair.py                    |  42 +++++
f778fe
 pcs/lib/cib/test/test_resource_common.py           |  16 +-
f778fe
 pcs/lib/cib/tools.py                               |  10 +-
f778fe
 pcs/lib/commands/resource.py                       |  86 +++++++---
f778fe
 pcs/lib/commands/test/resource/fixture.py          |   2 +-
f778fe
 .../commands/test/resource/test_bundle_create.py   | 179 +++++++++++++++----
f778fe
 .../commands/test/resource/test_bundle_update.py   | 102 ++++++++++-
f778fe
 .../test/resource/test_resource_enable_disable.py  |  93 ++++++++--
f778fe
 .../test/resource/test_resource_manage_unmanage.py | 189 +++++++++++++++++++--
f778fe
 pcs/lib/pacemaker/state.py                         |  40 ++++-
f778fe
 pcs/lib/pacemaker/test/test_state.py               | 108 +++++++++++-
f778fe
 pcs/pcs.8                                          |   6 +-
f778fe
 pcs/resource.py                                    |  99 ++++++++---
f778fe
 pcs/test/cib_resource/test_bundle.py               |  67 ++++++++
f778fe
 pcs/test/cib_resource/test_manage_unmanage.py      |   5 +-
f778fe
 pcs/test/test_resource.py                          |  40 +++--
f778fe
 pcs/usage.py                                       |   9 +-
f778fe
 22 files changed, 1055 insertions(+), 181 deletions(-)
f778fe
f778fe
diff --git a/pcs/cli/resource/parse_args.py b/pcs/cli/resource/parse_args.py
f778fe
index 19ee8f9..366acac 100644
f778fe
--- a/pcs/cli/resource/parse_args.py
f778fe
+++ b/pcs/cli/resource/parse_args.py
f778fe
@@ -58,7 +58,7 @@ def parse_create(arg_list):
f778fe
 
f778fe
 def _parse_bundle_groups(arg_list):
f778fe
     repeatable_keyword_list = ["port-map", "storage-map"]
f778fe
-    keyword_list = ["container", "network"] + repeatable_keyword_list
f778fe
+    keyword_list = ["meta", "container", "network"] + repeatable_keyword_list
f778fe
     groups = group_by_keywords(
f778fe
         arg_list,
f778fe
         set(keyword_list),
f778fe
@@ -99,6 +99,7 @@ def parse_bundle_create_options(arg_list):
f778fe
             prepare_options(storage_map)
f778fe
             for storage_map in groups.get("storage-map", [])
f778fe
         ],
f778fe
+        "meta": prepare_options(groups.get("meta", []))
f778fe
     }
f778fe
     if not parts["container_type"]:
f778fe
         parts["container_type"] = "docker"
f778fe
@@ -144,6 +145,7 @@ def parse_bundle_update_options(arg_list):
f778fe
         "port_map_remove": port_map["remove"],
f778fe
         "storage_map_add": storage_map["add"],
f778fe
         "storage_map_remove": storage_map["remove"],
f778fe
+        "meta": prepare_options(groups.get("meta", []))
f778fe
     }
f778fe
     return parts
f778fe
 
f778fe
diff --git a/pcs/cli/resource/test/test_parse_args.py b/pcs/cli/resource/test/test_parse_args.py
f778fe
index 5033ec7..0c936cc 100644
f778fe
--- a/pcs/cli/resource/test/test_parse_args.py
f778fe
+++ b/pcs/cli/resource/test/test_parse_args.py
f778fe
@@ -220,6 +220,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [],
f778fe
                 "storage_map": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -235,6 +236,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [],
f778fe
                 "storage_map": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -247,6 +249,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [],
f778fe
                 "storage_map": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -259,6 +262,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [],
f778fe
                 "storage_map": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -280,6 +284,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {"a": "b", "c": "d"},
f778fe
                 "port_map": [],
f778fe
                 "storage_map": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -309,6 +314,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [{"a": "b", "c": "d"}],
f778fe
                 "storage_map": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -321,6 +327,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [{"a": "b", "c": "d"}, {"e": "f"}],
f778fe
                 "storage_map": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -347,6 +354,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [],
f778fe
                 "storage_map": [{"a": "b", "c": "d"}],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -359,6 +367,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {},
f778fe
                 "port_map": [],
f778fe
                 "storage_map": [{"a": "b", "c": "d"}, {"e": "f"}],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -368,6 +377,28 @@ class ParseBundleCreateOptions(TestCase):
f778fe
     def test_storage_map_missing_key(self):
f778fe
         self.assert_raises_cmdline(["storage-map", "=b", "c=d"])
f778fe
 
f778fe
+    def test_meta(self):
f778fe
+        self.assert_produce(
f778fe
+            ["meta", "a=b", "c=d"],
f778fe
+            {
f778fe
+                "container_type": "docker",
f778fe
+                "container": {},
f778fe
+                "network": {},
f778fe
+                "port_map": [],
f778fe
+                "storage_map": [],
f778fe
+                "meta": {"a": "b", "c": "d"},
f778fe
+            }
f778fe
+        )
f778fe
+
f778fe
+    def test_meta_empty(self):
f778fe
+        self.assert_raises_cmdline(["meta"])
f778fe
+
f778fe
+    def test_meta_missing_value(self):
f778fe
+        self.assert_raises_cmdline(["meta", "a", "c=d"])
f778fe
+
f778fe
+    def test_meta_missing_key(self):
f778fe
+        self.assert_raises_cmdline(["meta", "=b", "c=d"])
f778fe
+
f778fe
     def test_all(self):
f778fe
         self.assert_produce(
f778fe
             [
f778fe
@@ -377,6 +408,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "port-map", "m=n", "o=p",
f778fe
                 "storage-map", "q=r", "s=t",
f778fe
                 "storage-map", "u=v", "w=x",
f778fe
+                "meta", "y=z", "A=B",
f778fe
             ],
f778fe
             {
f778fe
                 "container_type": "lxc",
f778fe
@@ -384,6 +416,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {"e": "f", "g": "h"},
f778fe
                 "port_map": [{"i": "j", "k": "l"}, {"m": "n", "o": "p"}],
f778fe
                 "storage_map": [{"q": "r", "s": "t"}, {"u": "v", "w": "x"}],
f778fe
+                "meta": {"y": "z", "A": "B"},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -391,11 +424,13 @@ class ParseBundleCreateOptions(TestCase):
f778fe
         self.assert_produce(
f778fe
             [
f778fe
                 "storage-map", "q=r", "s=t",
f778fe
+                "meta", "y=z",
f778fe
                 "port-map", "i=j", "k=l",
f778fe
                 "network", "e=f",
f778fe
                 "container", "lxc", "a=b",
f778fe
                 "storage-map", "u=v", "w=x",
f778fe
                 "port-map", "m=n", "o=p",
f778fe
+                "meta", "A=B",
f778fe
                 "network", "g=h",
f778fe
                 "container", "c=d",
f778fe
             ],
f778fe
@@ -405,6 +440,7 @@ class ParseBundleCreateOptions(TestCase):
f778fe
                 "network": {"e": "f", "g": "h"},
f778fe
                 "port_map": [{"i": "j", "k": "l"}, {"m": "n", "o": "p"}],
f778fe
                 "storage_map": [{"q": "r", "s": "t"}, {"u": "v", "w": "x"}],
f778fe
+                "meta": {"y": "z", "A": "B"},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -432,6 +468,7 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                 "port_map_remove": [],
f778fe
                 "storage_map_add": [],
f778fe
                 "storage_map_remove": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -445,6 +482,7 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                 "port_map_remove": [],
f778fe
                 "storage_map_add": [],
f778fe
                 "storage_map_remove": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -467,6 +505,7 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                 "port_map_remove": [],
f778fe
                 "storage_map_add": [],
f778fe
                 "storage_map_remove": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -519,6 +558,7 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                 "port_map_remove": ["c", "d", "i"],
f778fe
                 "storage_map_add": [],
f778fe
                 "storage_map_remove": [],
f778fe
+                "meta": {},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -562,9 +602,34 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                     {"e": "f", "g": "h",},
f778fe
                 ],
f778fe
                 "storage_map_remove": ["c", "d", "i"],
f778fe
+                "meta": {},
f778fe
+            }
f778fe
+        )
f778fe
+
f778fe
+    def test_meta(self):
f778fe
+        self.assert_produce(
f778fe
+            ["meta", "a=b", "c=d"],
f778fe
+            {
f778fe
+                "container": {},
f778fe
+                "network": {},
f778fe
+                "port_map_add": [],
f778fe
+                "port_map_remove": [],
f778fe
+                "storage_map_add": [],
f778fe
+                "storage_map_remove": [],
f778fe
+                "meta": {"a": "b", "c": "d"},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
+    def test_meta_empty(self):
f778fe
+        self.assert_raises_cmdline(["meta"])
f778fe
+
f778fe
+    def test_meta_missing_value(self):
f778fe
+        self.assert_raises_cmdline(["meta", "a", "c=d"])
f778fe
+
f778fe
+    def test_meta_missing_key(self):
f778fe
+        self.assert_raises_cmdline(["meta", "=b", "c=d"])
f778fe
+
f778fe
+
f778fe
     def test_all(self):
f778fe
         self.assert_produce(
f778fe
             [
f778fe
@@ -578,6 +643,7 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                 "storage-map", "add", "v=w",
f778fe
                 "storage-map", "remove", "x", "y",
f778fe
                 "storage-map", "remove", "z",
f778fe
+                "meta", "A=B", "C=D",
f778fe
             ],
f778fe
             {
f778fe
                 "container": {"a": "b", "c": "d"},
f778fe
@@ -592,6 +658,7 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                     {"v": "w"},
f778fe
                 ],
f778fe
                 "storage_map_remove": ["x", "y", "z"],
f778fe
+                "meta": {"A": "B", "C": "D"},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
@@ -599,11 +666,13 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
         self.assert_produce(
f778fe
             [
f778fe
                 "storage-map", "remove", "x", "y",
f778fe
+                "meta", "A=B",
f778fe
                 "port-map", "remove", "o", "p",
f778fe
                 "network", "e=f", "g=h",
f778fe
                 "storage-map", "add", "r=s", "t=u",
f778fe
                 "port-map", "add", "i=j", "k=l",
f778fe
                 "container", "a=b", "c=d",
f778fe
+                "meta", "C=D",
f778fe
                 "port-map", "remove", "q",
f778fe
                 "storage-map", "remove", "z",
f778fe
                 "storage-map", "add", "v=w",
f778fe
@@ -622,6 +691,7 @@ class ParseBundleUpdateOptions(TestCase):
f778fe
                     {"v": "w"},
f778fe
                 ],
f778fe
                 "storage_map_remove": ["x", "y", "z"],
f778fe
+                "meta": {"A": "B", "C": "D"},
f778fe
             }
f778fe
         )
f778fe
 
f778fe
diff --git a/pcs/lib/cib/nvpair.py b/pcs/lib/cib/nvpair.py
f778fe
index 261d17c..d3f5a5c 100644
f778fe
--- a/pcs/lib/cib/nvpair.py
f778fe
+++ b/pcs/lib/cib/nvpair.py
f778fe
@@ -11,18 +11,19 @@ from functools import partial
f778fe
 from pcs.lib.cib.tools import create_subelement_id
f778fe
 from pcs.lib.xml_tools import get_sub_element
f778fe
 
f778fe
-def _append_new_nvpair(nvset_element, name, value):
f778fe
+def _append_new_nvpair(nvset_element, name, value, id_provider=None):
f778fe
     """
f778fe
     Create nvpair with name and value as subelement of nvset_element.
f778fe
 
f778fe
     etree.Element nvset_element is context of new nvpair
f778fe
     string name is name attribute of new nvpair
f778fe
     string value is value attribute of new nvpair
f778fe
+    IdProvider id_provider -- elements' ids generator
f778fe
     """
f778fe
     etree.SubElement(
f778fe
         nvset_element,
f778fe
         "nvpair",
f778fe
-        id=create_subelement_id(nvset_element, name),
f778fe
+        id=create_subelement_id(nvset_element, name, id_provider),
f778fe
         name=name,
f778fe
         value=value
f778fe
     )
f778fe
@@ -73,7 +74,7 @@ def arrange_first_nvset(tag_name, context_element, nvpair_dict):
f778fe
 
f778fe
     update_nvset(nvset_element, nvpair_dict)
f778fe
 
f778fe
-def append_new_nvset(tag_name, context_element, nvpair_dict):
f778fe
+def append_new_nvset(tag_name, context_element, nvpair_dict, id_provider=None):
f778fe
     """
f778fe
     Append new nvset_element comprising nvpairs children (corresponding
f778fe
     nvpair_dict) to the context_element
f778fe
@@ -81,12 +82,13 @@ def append_new_nvset(tag_name, context_element, nvpair_dict):
f778fe
     string tag_name should be "instance_attributes" or "meta_attributes"
f778fe
     etree.Element context_element is element where new nvset will be appended
f778fe
     dict nvpair_dict contains source for nvpair children
f778fe
+    IdProvider id_provider -- elements' ids generator
f778fe
     """
f778fe
     nvset_element = etree.SubElement(context_element, tag_name, {
f778fe
-        "id": create_subelement_id(context_element, tag_name)
f778fe
+        "id": create_subelement_id(context_element, tag_name, id_provider)
f778fe
     })
f778fe
     for name, value in sorted(nvpair_dict.items()):
f778fe
-        _append_new_nvpair(nvset_element, name, value)
f778fe
+        _append_new_nvpair(nvset_element, name, value, id_provider)
f778fe
 
f778fe
 append_new_instance_attributes = partial(
f778fe
     append_new_nvset,
f778fe
diff --git a/pcs/lib/cib/resource/bundle.py b/pcs/lib/cib/resource/bundle.py
f778fe
index 0fe16f3..8a49c28 100644
f778fe
--- a/pcs/lib/cib/resource/bundle.py
f778fe
+++ b/pcs/lib/cib/resource/bundle.py
f778fe
@@ -9,6 +9,10 @@ from lxml import etree
f778fe
 
f778fe
 from pcs.common import report_codes
f778fe
 from pcs.lib import reports, validate
f778fe
+from pcs.lib.cib.nvpair import (
f778fe
+    append_new_meta_attributes,
f778fe
+    arrange_first_meta_attributes,
f778fe
+)
f778fe
 from pcs.lib.cib.resource.primitive import TAG as TAG_PRIMITIVE
f778fe
 from pcs.lib.cib.tools import find_element_by_tag_and_id
f778fe
 from pcs.lib.errors import (
f778fe
@@ -96,7 +100,7 @@ def validate_new(
f778fe
 
f778fe
 def append_new(
f778fe
     parent_element, id_provider, bundle_id, container_type, container_options,
f778fe
-    network_options, port_map, storage_map
f778fe
+    network_options, port_map, storage_map, meta_attributes
f778fe
 ):
f778fe
     """
f778fe
     Create new bundle and add it to the CIB
f778fe
@@ -109,6 +113,7 @@ def append_new(
f778fe
     dict network_options -- network options
f778fe
     list of dict port_map -- list of port mapping options
f778fe
     list of dict storage_map -- list of storage mapping options
f778fe
+    dict meta_attributes -- meta attributes
f778fe
     """
f778fe
     bundle_element = etree.SubElement(parent_element, TAG, {"id": bundle_id})
f778fe
     # TODO create the proper element once more container_types are supported
f778fe
@@ -132,6 +137,8 @@ def append_new(
f778fe
         _append_storage_map(
f778fe
             storage_element, id_provider, bundle_id, storage_map_options
f778fe
         )
f778fe
+    if meta_attributes:
f778fe
+        append_new_meta_attributes(bundle_element, meta_attributes, id_provider)
f778fe
     return bundle_element
f778fe
 
f778fe
 def validate_update(
f778fe
@@ -203,7 +210,8 @@ def validate_update(
f778fe
 
f778fe
 def update(
f778fe
     id_provider, bundle_el, container_options, network_options,
f778fe
-    port_map_add, port_map_remove, storage_map_add, storage_map_remove
f778fe
+    port_map_add, port_map_remove, storage_map_add, storage_map_remove,
f778fe
+    meta_attributes
f778fe
 ):
f778fe
     """
f778fe
     Modify an existing bundle (does not touch encapsulated resources)
f778fe
@@ -216,6 +224,7 @@ def update(
f778fe
     list of string port_map_remove -- list of port mapping ids to remove
f778fe
     list of dict storage_map_add -- list of storage mapping options to add
f778fe
     list of string storage_map_remove -- list of storage mapping ids to remove
f778fe
+    dict meta_attributes -- meta attributes to update
f778fe
     """
f778fe
     bundle_id = bundle_el.get("id")
f778fe
     update_attributes_remove_empty(
f778fe
@@ -253,7 +262,11 @@ def update(
f778fe
             storage_element, id_provider, bundle_id, storage_map_options
f778fe
         )
f778fe
 
f778fe
+    if meta_attributes:
f778fe
+        arrange_first_meta_attributes(bundle_el, meta_attributes)
f778fe
+
f778fe
     # remove empty elements with no attributes
f778fe
+    # meta attributes are handled in their own function
f778fe
     for element in (network_element, storage_element):
f778fe
         if len(element) < 1 and not element.attrib:
f778fe
             element.getparent().remove(element)
f778fe
diff --git a/pcs/lib/cib/resource/common.py b/pcs/lib/cib/resource/common.py
f778fe
index f9028ff..0e52b4c 100644
f778fe
--- a/pcs/lib/cib/resource/common.py
f778fe
+++ b/pcs/lib/cib/resource/common.py
f778fe
@@ -58,16 +58,18 @@ def find_resources_to_enable(resource_el):
f778fe
     etree resource_el -- resource element
f778fe
     """
f778fe
     if is_bundle(resource_el):
f778fe
-        # bundles currently cannot be disabled - pcmk does not support that
f778fe
-        # inner resources are supposed to be managed separately
f778fe
-        return []
f778fe
+        to_enable = [resource_el]
f778fe
+        in_bundle = get_bundle_inner_resource(resource_el)
f778fe
+        if in_bundle is not None:
f778fe
+            to_enable.append(in_bundle)
f778fe
+        return to_enable
f778fe
 
f778fe
     if is_any_clone(resource_el):
f778fe
         return [resource_el, get_clone_inner_resource(resource_el)]
f778fe
 
f778fe
     to_enable = [resource_el]
f778fe
     parent = resource_el.getparent()
f778fe
-    if is_any_clone(parent):
f778fe
+    if is_any_clone(parent) or is_bundle(parent):
f778fe
         to_enable.append(parent)
f778fe
     return to_enable
f778fe
 
f778fe
@@ -109,20 +111,25 @@ def find_resources_to_manage(resource_el):
f778fe
     # put there manually. If we didn't do it, the resource may stay unmanaged,
f778fe
     # as a managed primitive in an unmanaged clone / group is still unmanaged
f778fe
     # and vice versa.
f778fe
-    # Bundle resources cannot be set as unmanaged - pcmk currently doesn't
f778fe
-    # support that. Resources in a bundle are supposed to be treated separately.
f778fe
-    if is_bundle(resource_el):
f778fe
-        return []
f778fe
     res_id = resource_el.attrib["id"]
f778fe
     return (
f778fe
         [resource_el] # the resource itself
f778fe
         +
f778fe
         # its parents
f778fe
         find_parent(resource_el, "resources").xpath(
f778fe
+            # a master or a clone which contains a group, a primitve, or a
f778fe
+            # grouped primitive with the specified id
f778fe
+            # OR
f778fe
+            # a group (in a clone, master, etc. - hence //) which contains a
f778fe
+            # primitive with the specified id
f778fe
+            # OR
f778fe
+            # a bundle which contains a primitive with the specified id
f778fe
             """
f778fe
                 (./master|./clone)[(group|group/primitive|primitive)[@id='{r}']]
f778fe
                 |
f778fe
                 //group[primitive[@id='{r}']]
f778fe
+                |
f778fe
+                ./bundle[primitive[@id='{r}']]
f778fe
             """
f778fe
             .format(r=res_id)
f778fe
         )
f778fe
@@ -164,10 +171,19 @@ def find_resources_to_unmanage(resource_el):
f778fe
     #   See clone notes above
f778fe
     #
f778fe
     # a bundled primitive - the primitive - the primitive
f778fe
-    # a bundled primitive - the bundle - nothing
f778fe
-    #  bundles currently cannot be set as unmanaged - pcmk does not support that
f778fe
-    # an empty bundle - the bundle - nothing
f778fe
-    #  bundles currently cannot be set as unmanaged - pcmk does not support that
f778fe
+    # a bundled primitive - the bundle - the bundle and the primitive
f778fe
+    #  We need to unmanage implicit resources create by pacemaker and there is
f778fe
+    #  no other way to do it than unmanage the bundle itself.
f778fe
+    #  Since it is not possible to unbundle a resource, the concers described
f778fe
+    #  at unclone don't apply here. However to prevent future bugs, in case
f778fe
+    #  unbundling becomes possible, we unmanage the primitive as well.
f778fe
+    # an empty bundle - the bundle - the bundle
f778fe
+    #  There is nothing else to unmanage.
f778fe
+    if is_bundle(resource_el):
f778fe
+        in_bundle = get_bundle_inner_resource(resource_el)
f778fe
+        return (
f778fe
+            [resource_el, in_bundle] if in_bundle is not None else [resource_el]
f778fe
+        )
f778fe
     if is_any_clone(resource_el):
f778fe
         resource_el = get_clone_inner_resource(resource_el)
f778fe
     if is_group(resource_el):
f778fe
diff --git a/pcs/lib/cib/test/test_nvpair.py b/pcs/lib/cib/test/test_nvpair.py
f778fe
index 9b9d9b9..0f6d8f8 100644
f778fe
--- a/pcs/lib/cib/test/test_nvpair.py
f778fe
+++ b/pcs/lib/cib/test/test_nvpair.py
f778fe
@@ -8,6 +8,7 @@ from __future__ import (
f778fe
 from lxml import etree
f778fe
 
f778fe
 from pcs.lib.cib import nvpair
f778fe
+from pcs.lib.cib.tools import IdProvider
f778fe
 from pcs.test.tools.assertions import assert_xml_equal
f778fe
 from pcs.test.tools.pcs_unittest import TestCase, mock
f778fe
 from pcs.test.tools.xml import etree_to_str
f778fe
@@ -25,6 +26,21 @@ class AppendNewNvpair(TestCase):
f778fe
             """
f778fe
         )
f778fe
 
f778fe
+    def test_with_id_provider(self):
f778fe
+        nvset_element = etree.fromstring('<nvset id="a"/>')
f778fe
+        provider = IdProvider(nvset_element)
f778fe
+        provider.book_ids("a-b")
f778fe
+        nvpair._append_new_nvpair(nvset_element, "b", "c", provider)
f778fe
+        assert_xml_equal(
f778fe
+            etree_to_str(nvset_element),
f778fe
+            """
f778fe
+            <nvset id="a">
f778fe
+                <nvpair id="a-b-1" name="b" value="c"></nvpair>
f778fe
+            </nvset>
f778fe
+            """
f778fe
+        )
f778fe
+
f778fe
+
f778fe
 class UpdateNvsetTest(TestCase):
f778fe
     @mock.patch(
f778fe
         "pcs.lib.cib.nvpair.create_subelement_id",
f778fe
@@ -167,6 +183,32 @@ class AppendNewNvsetTest(TestCase):
f778fe
             etree_to_str(context_element)
f778fe
         )
f778fe
 
f778fe
+    def test_with_id_provider(self):
f778fe
+        context_element = etree.fromstring('<context id="a"/>')
f778fe
+        provider = IdProvider(context_element)
f778fe
+        provider.book_ids("a-instance_attributes", "a-instance_attributes-1-a")
f778fe
+        nvpair.append_new_nvset(
f778fe
+            "instance_attributes",
f778fe
+            context_element,
f778fe
+            {
f778fe
+                "a": "b",
f778fe
+                "c": "d",
f778fe
+            },
f778fe
+            provider
f778fe
+        )
f778fe
+        assert_xml_equal(
f778fe
+            """
f778fe
+                <context id="a">
f778fe
+                    <instance_attributes id="a-instance_attributes-1">
f778fe
+                        <nvpair id="a-instance_attributes-1-a-1" name="a" value="b"/>
f778fe
+                        <nvpair id="a-instance_attributes-1-c" name="c" value="d"/>
f778fe
+                    </instance_attributes>
f778fe
+                </context>
f778fe
+            """,
f778fe
+            etree_to_str(context_element)
f778fe
+        )
f778fe
+
f778fe
+
f778fe
 class ArrangeFirstNvsetTest(TestCase):
f778fe
     def setUp(self):
f778fe
         self.root = etree.Element("root", id="root")
f778fe
diff --git a/pcs/lib/cib/test/test_resource_common.py b/pcs/lib/cib/test/test_resource_common.py
f778fe
index 52c2329..6b485f7 100644
f778fe
--- a/pcs/lib/cib/test/test_resource_common.py
f778fe
+++ b/pcs/lib/cib/test/test_resource_common.py
f778fe
@@ -180,7 +180,7 @@ class FindResourcesToEnable(TestCase):
f778fe
         self.assert_find_resources("F2", ["F2"])
f778fe
 
f778fe
     def test_primitive_in_bundle(self):
f778fe
-        self.assert_find_resources("H", ["H"])
f778fe
+        self.assert_find_resources("H", ["H", "H-bundle"])
f778fe
 
f778fe
     def test_group(self):
f778fe
         self.assert_find_resources("D", ["D"])
f778fe
@@ -204,10 +204,10 @@ class FindResourcesToEnable(TestCase):
f778fe
         self.assert_find_resources("F-master", ["F-master", "F"])
f778fe
 
f778fe
     def test_bundle_empty(self):
f778fe
-        self.assert_find_resources("G-bundle", [])
f778fe
+        self.assert_find_resources("G-bundle", ["G-bundle"])
f778fe
 
f778fe
     def test_bundle_with_primitive(self):
f778fe
-        self.assert_find_resources("H-bundle", [])
f778fe
+        self.assert_find_resources("H-bundle", ["H-bundle", "H"])
f778fe
 
f778fe
 
f778fe
 class Enable(TestCase):
f778fe
@@ -360,7 +360,7 @@ class FindResourcesToManage(TestCase):
f778fe
         self.assert_find_resources("F2", ["F2", "F-master", "F"])
f778fe
 
f778fe
     def test_primitive_in_bundle(self):
f778fe
-        self.assert_find_resources("H", ["H"])
f778fe
+        self.assert_find_resources("H", ["H", "H-bundle"])
f778fe
 
f778fe
     def test_group(self):
f778fe
         self.assert_find_resources("D", ["D", "D1", "D2"])
f778fe
@@ -384,10 +384,10 @@ class FindResourcesToManage(TestCase):
f778fe
         self.assert_find_resources("F-master", ["F-master", "F", "F1", "F2"])
f778fe
 
f778fe
     def test_bundle_empty(self):
f778fe
-        self.assert_find_resources("G-bundle", [])
f778fe
+        self.assert_find_resources("G-bundle", ["G-bundle"])
f778fe
 
f778fe
     def test_bundle_with_primitive(self):
f778fe
-        self.assert_find_resources("H-bundle", [])
f778fe
+        self.assert_find_resources("H-bundle", ["H-bundle", "H"])
f778fe
 
f778fe
 
f778fe
 class FindResourcesToUnmanage(TestCase):
f778fe
@@ -447,10 +447,10 @@ class FindResourcesToUnmanage(TestCase):
f778fe
         self.assert_find_resources("F-master", ["F1", "F2"])
f778fe
 
f778fe
     def test_bundle_empty(self):
f778fe
-        self.assert_find_resources("G-bundle", [])
f778fe
+        self.assert_find_resources("G-bundle", ["G-bundle"])
f778fe
 
f778fe
     def test_bundle_with_primitive(self):
f778fe
-        self.assert_find_resources("H-bundle", [])
f778fe
+        self.assert_find_resources("H-bundle", ["H-bundle", "H"])
f778fe
 
f778fe
 
f778fe
 class Manage(TestCase):
f778fe
diff --git a/pcs/lib/cib/tools.py b/pcs/lib/cib/tools.py
f778fe
index 2308a42..cf91125 100644
f778fe
--- a/pcs/lib/cib/tools.py
f778fe
+++ b/pcs/lib/cib/tools.py
f778fe
@@ -177,11 +177,11 @@ def find_element_by_tag_and_id(
f778fe
         )
f778fe
     )
f778fe
 
f778fe
-def create_subelement_id(context_element, suffix):
f778fe
-    return find_unique_id(
f778fe
-        context_element,
f778fe
-        "{0}-{1}".format(context_element.get("id"), suffix)
f778fe
-    )
f778fe
+def create_subelement_id(context_element, suffix, id_provider=None):
f778fe
+    proposed_id = "{0}-{1}".format(context_element.get("id"), suffix)
f778fe
+    if id_provider:
f778fe
+        return id_provider.allocate_id(proposed_id)
f778fe
+    return find_unique_id(context_element, proposed_id)
f778fe
 
f778fe
 def check_new_id_applicable(tree, description, id):
f778fe
     validate_id(id, description)
f778fe
diff --git a/pcs/lib/commands/resource.py b/pcs/lib/commands/resource.py
f778fe
index 3a060b8..0c5f682 100644
f778fe
--- a/pcs/lib/commands/resource.py
f778fe
+++ b/pcs/lib/commands/resource.py
f778fe
@@ -22,6 +22,7 @@ from pcs.lib.errors import LibraryError
f778fe
 from pcs.lib.pacemaker.values import validate_id
f778fe
 from pcs.lib.pacemaker.state import (
f778fe
     ensure_resource_state,
f778fe
+    info_resource_state,
f778fe
     is_resource_managed,
f778fe
     ResourceNotFound,
f778fe
 )
f778fe
@@ -31,7 +32,10 @@ from pcs.lib.resource_agent import(
f778fe
 
f778fe
 @contextmanager
f778fe
 def resource_environment(
f778fe
-    env, wait=False, wait_for_resource_ids=None, disabled_after_wait=False,
f778fe
+    env,
f778fe
+    wait=False,
f778fe
+    wait_for_resource_ids=None,
f778fe
+    resource_state_reporter=info_resource_state,
f778fe
     required_cib_version=None
f778fe
 ):
f778fe
     env.ensure_wait_satisfiable(wait)
f778fe
@@ -41,10 +45,19 @@ def resource_environment(
f778fe
     if wait is not False and wait_for_resource_ids:
f778fe
         state = env.get_cluster_state()
f778fe
         env.report_processor.process_list([
f778fe
-            ensure_resource_state(not disabled_after_wait, state, res_id)
f778fe
+            resource_state_reporter(state, res_id)
f778fe
             for res_id in wait_for_resource_ids
f778fe
         ])
f778fe
 
f778fe
+def _ensure_disabled_after_wait(disabled_after_wait):
f778fe
+    def inner(state, resource_id):
f778fe
+        return ensure_resource_state(
f778fe
+            not disabled_after_wait,
f778fe
+            state,
f778fe
+            resource_id
f778fe
+        )
f778fe
+    return inner
f778fe
+
f778fe
 def _validate_remote_connection(
f778fe
     resource_agent, nodes_to_validate_against, resource_id, instance_attributes,
f778fe
     allow_not_suitable_command
f778fe
@@ -195,7 +208,11 @@ def create(
f778fe
         env,
f778fe
         wait,
f778fe
         [resource_id],
f778fe
-        ensure_disabled or resource.common.are_meta_disabled(meta_attributes),
f778fe
+        _ensure_disabled_after_wait(
f778fe
+            ensure_disabled
f778fe
+            or
f778fe
+            resource.common.are_meta_disabled(meta_attributes)
f778fe
+        )
f778fe
     ) as resources_section:
f778fe
         _check_special_cases(
f778fe
             env,
f778fe
@@ -269,7 +286,7 @@ def _create_as_clone_common(
f778fe
         env,
f778fe
         wait,
f778fe
         [resource_id],
f778fe
-        (
f778fe
+        _ensure_disabled_after_wait(
f778fe
             ensure_disabled
f778fe
             or
f778fe
             resource.common.are_meta_disabled(meta_attributes)
f778fe
@@ -353,7 +370,11 @@ def create_in_group(
f778fe
         env,
f778fe
         wait,
f778fe
         [resource_id],
f778fe
-        ensure_disabled or resource.common.are_meta_disabled(meta_attributes),
f778fe
+        _ensure_disabled_after_wait(
f778fe
+            ensure_disabled
f778fe
+            or
f778fe
+            resource.common.are_meta_disabled(meta_attributes)
f778fe
+        )
f778fe
     ) as resources_section:
f778fe
         _check_special_cases(
f778fe
             env,
f778fe
@@ -433,7 +454,11 @@ def create_into_bundle(
f778fe
         env,
f778fe
         wait,
f778fe
         [resource_id],
f778fe
-        disabled_after_wait=ensure_disabled,
f778fe
+        _ensure_disabled_after_wait(
f778fe
+            ensure_disabled
f778fe
+            or
f778fe
+            resource.common.are_meta_disabled(meta_attributes)
f778fe
+        ),
f778fe
         required_cib_version=(2, 8, 0)
f778fe
     ) as resources_section:
f778fe
         _check_special_cases(
f778fe
@@ -465,8 +490,9 @@ def create_into_bundle(
f778fe
 
f778fe
 def bundle_create(
f778fe
     env, bundle_id, container_type, container_options=None,
f778fe
-    network_options=None, port_map=None, storage_map=None,
f778fe
+    network_options=None, port_map=None, storage_map=None, meta_attributes=None,
f778fe
     force_options=False,
f778fe
+    ensure_disabled=False,
f778fe
     wait=False,
f778fe
 ):
f778fe
     """
f778fe
@@ -477,24 +503,32 @@ def bundle_create(
f778fe
     string container_type -- container engine name (docker, lxc...)
f778fe
     dict container_options -- container options
f778fe
     dict network_options -- network options
f778fe
-    list of dict port_map -- list of port mapping options
f778fe
-    list of dict storage_map -- list of storage mapping options
f778fe
+    list of dict port_map -- a list of port mapping options
f778fe
+    list of dict storage_map -- a list of storage mapping options
f778fe
+    dict meta_attributes -- bundle's meta attributes
f778fe
     bool force_options -- return warnings instead of forceable errors
f778fe
+    bool ensure_disabled -- set the bundle's target-role to "Stopped"
f778fe
     mixed wait -- False: no wait, None: wait default timeout, int: wait timeout
f778fe
     """
f778fe
     container_options = container_options or {}
f778fe
     network_options = network_options or {}
f778fe
     port_map = port_map or []
f778fe
     storage_map = storage_map or []
f778fe
+    meta_attributes = meta_attributes or {}
f778fe
 
f778fe
     with resource_environment(
f778fe
         env,
f778fe
         wait,
f778fe
         [bundle_id],
f778fe
-        # bundles are always enabled, currently there is no way to disable them
f778fe
-        disabled_after_wait=False,
f778fe
+        _ensure_disabled_after_wait(
f778fe
+            ensure_disabled
f778fe
+            or
f778fe
+            resource.common.are_meta_disabled(meta_attributes)
f778fe
+        ),
f778fe
         required_cib_version=(2, 8, 0)
f778fe
     ) as resources_section:
f778fe
+        # no need to run validations related to remote and guest nodes as those
f778fe
+        # nodes can only be created from primitive resources
f778fe
         id_provider = IdProvider(resources_section)
f778fe
         env.report_processor.process_list(
f778fe
             resource.bundle.validate_new(
f778fe
@@ -505,10 +539,11 @@ def bundle_create(
f778fe
                 network_options,
f778fe
                 port_map,
f778fe
                 storage_map,
f778fe
+                # TODO meta attributes - there is no validation for now
f778fe
                 force_options
f778fe
             )
f778fe
         )
f778fe
-        resource.bundle.append_new(
f778fe
+        bundle_element = resource.bundle.append_new(
f778fe
             resources_section,
f778fe
             id_provider,
f778fe
             bundle_id,
f778fe
@@ -516,13 +551,16 @@ def bundle_create(
f778fe
             container_options,
f778fe
             network_options,
f778fe
             port_map,
f778fe
-            storage_map
f778fe
+            storage_map,
f778fe
+            meta_attributes
f778fe
         )
f778fe
+        if ensure_disabled:
f778fe
+            resource.common.disable(bundle_element)
f778fe
 
f778fe
 def bundle_update(
f778fe
     env, bundle_id, container_options=None, network_options=None,
f778fe
     port_map_add=None, port_map_remove=None, storage_map_add=None,
f778fe
-    storage_map_remove=None,
f778fe
+    storage_map_remove=None, meta_attributes=None,
f778fe
     force_options=False,
f778fe
     wait=False,
f778fe
 ):
f778fe
@@ -537,6 +575,7 @@ def bundle_update(
f778fe
     list of string port_map_remove -- list of port mapping ids to remove
f778fe
     list of dict storage_map_add -- list of storage mapping options to add
f778fe
     list of string storage_map_remove -- list of storage mapping ids to remove
f778fe
+    dict meta_attributes -- meta attributes to update
f778fe
     bool force_options -- return warnings instead of forceable errors
f778fe
     mixed wait -- False: no wait, None: wait default timeout, int: wait timeout
f778fe
     """
f778fe
@@ -546,15 +585,16 @@ def bundle_update(
f778fe
     port_map_remove = port_map_remove or []
f778fe
     storage_map_add = storage_map_add or []
f778fe
     storage_map_remove = storage_map_remove or []
f778fe
+    meta_attributes = meta_attributes or {}
f778fe
 
f778fe
     with resource_environment(
f778fe
         env,
f778fe
         wait,
f778fe
         [bundle_id],
f778fe
-        # bundles are always enabled, currently there is no way to disable them
f778fe
-        disabled_after_wait=False,
f778fe
         required_cib_version=(2, 8, 0)
f778fe
     ) as resources_section:
f778fe
+        # no need to run validations related to remote and guest nodes as those
f778fe
+        # nodes can only be created from primitive resources
f778fe
         id_provider = IdProvider(resources_section)
f778fe
         bundle_element = find_element_by_tag_and_id(
f778fe
             resource.bundle.TAG,
f778fe
@@ -571,6 +611,7 @@ def bundle_update(
f778fe
                 port_map_remove,
f778fe
                 storage_map_add,
f778fe
                 storage_map_remove,
f778fe
+                # TODO meta attributes - there is no validation for now
f778fe
                 force_options
f778fe
             )
f778fe
         )
f778fe
@@ -582,7 +623,8 @@ def bundle_update(
f778fe
             port_map_add,
f778fe
             port_map_remove,
f778fe
             storage_map_add,
f778fe
-            storage_map_remove
f778fe
+            storage_map_remove,
f778fe
+            meta_attributes
f778fe
         )
f778fe
 
f778fe
 def disable(env, resource_ids, wait):
f778fe
@@ -593,7 +635,7 @@ def disable(env, resource_ids, wait):
f778fe
     mixed wait -- False: no wait, None: wait default timeout, int: wait timeout
f778fe
     """
f778fe
     with resource_environment(
f778fe
-        env, wait, resource_ids, True
f778fe
+        env, wait, resource_ids, _ensure_disabled_after_wait(True)
f778fe
     ) as resources_section:
f778fe
         resource_el_list = _find_resources_or_raise(
f778fe
             resources_section,
f778fe
@@ -615,7 +657,7 @@ def enable(env, resource_ids, wait):
f778fe
     mixed wait -- False: no wait, None: wait default timeout, int: wait timeout
f778fe
     """
f778fe
     with resource_environment(
f778fe
-        env, wait, resource_ids, False
f778fe
+        env, wait, resource_ids, _ensure_disabled_after_wait(False)
f778fe
     ) as resources_section:
f778fe
         resource_el_list = _find_resources_or_raise(
f778fe
             resources_section,
f778fe
@@ -642,7 +684,7 @@ def _resource_list_enable_disable(resource_el_list, func, cluster_state):
f778fe
             report_list.append(
f778fe
                 reports.id_not_found(
f778fe
                     res_id,
f778fe
-                    id_description="resource/clone/master/group"
f778fe
+                    id_description="resource/clone/master/group/bundle"
f778fe
                )
f778fe
             )
f778fe
     return report_list
f778fe
@@ -735,7 +777,7 @@ def _find_resources_or_raise(
f778fe
     resource_tags = (
f778fe
         resource.clone.ALL_TAGS
f778fe
         +
f778fe
-        [resource.group.TAG, resource.primitive.TAG]
f778fe
+        [resource.group.TAG, resource.primitive.TAG, resource.bundle.TAG]
f778fe
     )
f778fe
     for res_id in resource_ids:
f778fe
         try:
f778fe
@@ -745,7 +787,7 @@ def _find_resources_or_raise(
f778fe
                         resource_tags,
f778fe
                         resources_section,
f778fe
                         res_id,
f778fe
-                        id_description="resource/clone/master/group"
f778fe
+                        id_description="resource/clone/master/group/bundle"
f778fe
                     )
f778fe
                 )
f778fe
             )
f778fe
diff --git a/pcs/lib/commands/test/resource/fixture.py b/pcs/lib/commands/test/resource/fixture.py
f778fe
index f1fe09b..8d96dc9 100644
f778fe
--- a/pcs/lib/commands/test/resource/fixture.py
f778fe
+++ b/pcs/lib/commands/test/resource/fixture.py
f778fe
@@ -145,7 +145,7 @@ def report_not_found(res_id, context_type=""):
f778fe
             "context_type": context_type,
f778fe
             "context_id": "",
f778fe
             "id": res_id,
f778fe
-            "id_description": "resource/clone/master/group",
f778fe
+            "id_description": "resource/clone/master/group/bundle",
f778fe
         },
f778fe
         None
f778fe
     )
f778fe
diff --git a/pcs/lib/commands/test/resource/test_bundle_create.py b/pcs/lib/commands/test/resource/test_bundle_create.py
f778fe
index b9922d8..3bdeee9 100644
f778fe
--- a/pcs/lib/commands/test/resource/test_bundle_create.py
f778fe
+++ b/pcs/lib/commands/test/resource/test_bundle_create.py
f778fe
@@ -40,7 +40,7 @@ class MinimalCreate(CommonTest):
f778fe
             self.fixture_cib_pre,
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {"image": "pcs:test", }
f778fe
+                container_options={"image": "pcs:test", }
f778fe
             ),
f778fe
             self.fixture_resources_bundle_simple
f778fe
         )
f778fe
@@ -90,7 +90,7 @@ class MinimalCreate(CommonTest):
f778fe
 
f778fe
         resource.bundle_create(
f778fe
             self.env, "B1", "docker",
f778fe
-            {"image": "pcs:test", }
f778fe
+            container_options={"image": "pcs:test", }
f778fe
         )
f778fe
 
f778fe
         self.env.report_processor.assert_reports([
f778fe
@@ -122,7 +122,7 @@ class CreateDocker(CommonTest):
f778fe
             self.fixture_cib_pre,
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {"image": "pcs:test", }
f778fe
+                container_options={"image": "pcs:test", }
f778fe
             ),
f778fe
             self.fixture_resources_bundle_simple
f778fe
         )
f778fe
@@ -132,7 +132,7 @@ class CreateDocker(CommonTest):
f778fe
             self.fixture_cib_pre,
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {
f778fe
+                container_options={
f778fe
                     "image": "pcs:test",
f778fe
                     "masters": "0",
f778fe
                     "network": "extra network settings",
f778fe
@@ -168,7 +168,7 @@ class CreateDocker(CommonTest):
f778fe
         assert_raise_library_error(
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {
f778fe
+                container_options={
f778fe
                     "replicas-per-host": "0",
f778fe
                     "replicas": "0",
f778fe
                     "masters": "-1",
f778fe
@@ -226,7 +226,7 @@ class CreateDocker(CommonTest):
f778fe
         assert_raise_library_error(
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {
f778fe
+                container_options={
f778fe
                     "image": "",
f778fe
                 },
f778fe
                 force_options=True
f778fe
@@ -253,7 +253,7 @@ class CreateDocker(CommonTest):
f778fe
         assert_raise_library_error(
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {
f778fe
+                container_options={
f778fe
                     "image": "pcs:test",
f778fe
                     "extra": "option",
f778fe
                 }
f778fe
@@ -276,7 +276,7 @@ class CreateDocker(CommonTest):
f778fe
             self.fixture_cib_pre,
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {
f778fe
+                container_options={
f778fe
                     "image": "pcs:test",
f778fe
                     "extra": "option",
f778fe
                 },
f778fe
@@ -932,13 +932,61 @@ class CreateWithStorageMap(CommonTest):
f778fe
         )
f778fe
 
f778fe
 
f778fe
+class CreateWithMeta(CommonTest):
f778fe
+    def test_success(self):
f778fe
+        self.assert_command_effect(
f778fe
+            self.fixture_cib_pre,
f778fe
+            lambda: resource.bundle_create(
f778fe
+                self.env, "B1", "docker",
f778fe
+                container_options={"image": "pcs:test", },
f778fe
+                meta_attributes={
f778fe
+                    "target-role": "Stopped",
f778fe
+                    "is-managed": "false",
f778fe
+                }
f778fe
+            ),
f778fe
+            """
f778fe
+                <resources>
f778fe
+                    <bundle id="B1">
f778fe
+                        <docker image="pcs:test" />
f778fe
+                        <meta_attributes id="B1-meta_attributes">
f778fe
+                            
f778fe
+                                name="is-managed" value="false" />
f778fe
+                            
f778fe
+                                name="target-role" value="Stopped" />
f778fe
+                        </meta_attributes>
f778fe
+                    </bundle>
f778fe
+                </resources>
f778fe
+            """
f778fe
+        )
f778fe
+
f778fe
+    def test_disabled(self):
f778fe
+        self.assert_command_effect(
f778fe
+            self.fixture_cib_pre,
f778fe
+            lambda: resource.bundle_create(
f778fe
+                self.env, "B1", "docker",
f778fe
+                container_options={"image": "pcs:test", },
f778fe
+                ensure_disabled=True
f778fe
+            ),
f778fe
+            """
f778fe
+                <resources>
f778fe
+                    <bundle id="B1">
f778fe
+                        <meta_attributes id="B1-meta_attributes">
f778fe
+                            
f778fe
+                                name="target-role" value="Stopped" />
f778fe
+                        </meta_attributes>
f778fe
+                        <docker image="pcs:test" />
f778fe
+                    </bundle>
f778fe
+                </resources>
f778fe
+            """
f778fe
+        )
f778fe
+
f778fe
 class CreateWithAllOptions(CommonTest):
f778fe
     def test_success(self):
f778fe
         self.assert_command_effect(
f778fe
             self.fixture_cib_pre,
f778fe
             lambda: resource.bundle_create(
f778fe
                 self.env, "B1", "docker",
f778fe
-                {
f778fe
+                container_options={
f778fe
                     "image": "pcs:test",
f778fe
                     "masters": "0",
f778fe
                     "network": "extra network settings",
f778fe
@@ -947,13 +995,13 @@ class CreateWithAllOptions(CommonTest):
f778fe
                     "replicas": "4",
f778fe
                     "replicas-per-host": "2",
f778fe
                 },
f778fe
-                {
f778fe
+                network_options={
f778fe
                     "control-port": "12345",
f778fe
                     "host-interface": "eth0",
f778fe
                     "host-netmask": "24",
f778fe
                     "ip-range-start": "192.168.100.200",
f778fe
                 },
f778fe
-                [
f778fe
+                port_map=[
f778fe
                     {
f778fe
                         "port": "1001",
f778fe
                     },
f778fe
@@ -967,7 +1015,7 @@ class CreateWithAllOptions(CommonTest):
f778fe
                         "range": "3000-3300",
f778fe
                     },
f778fe
                 ],
f778fe
-                [
f778fe
+                storage_map=[
f778fe
                     {
f778fe
                         "source-dir": "/tmp/docker1a",
f778fe
                         "target-dir": "/tmp/docker1b",
f778fe
@@ -1082,21 +1130,26 @@ class Wait(CommonTest):
f778fe
         </resources>
f778fe
     """
f778fe
 
f778fe
-    timeout = 10
f778fe
+    fixture_resources_bundle_simple_disabled = """
f778fe
+        <resources>
f778fe
+            <bundle id="B1">
f778fe
+                <meta_attributes id="B1-meta_attributes">
f778fe
+                    
f778fe
+                        name="target-role" value="Stopped" />
f778fe
+                </meta_attributes>
f778fe
+                <docker image="pcs:test" />
f778fe
+            </bundle>
f778fe
+        </resources>
f778fe
+    """
f778fe
 
f778fe
-    def fixture_calls_initial(self):
f778fe
-        return (
f778fe
-            fixture.call_wait_supported() +
f778fe
-            fixture.calls_cib(
f778fe
-                self.fixture_cib_pre,
f778fe
-                self.fixture_resources_bundle_simple,
f778fe
-                cib_base_file=self.cib_base_file,
f778fe
-            )
f778fe
-        )
f778fe
+    timeout = 10
f778fe
 
f778fe
-    def simple_bundle_create(self, wait=False):
f778fe
+    def simple_bundle_create(self, wait=False, disabled=False):
f778fe
         return resource.bundle_create(
f778fe
-            self.env, "B1", "docker", {"image": "pcs:test"}, wait=wait,
f778fe
+            self.env, "B1", "docker",
f778fe
+            container_options={"image": "pcs:test"},
f778fe
+            ensure_disabled=disabled,
f778fe
+            wait=wait,
f778fe
         )
f778fe
 
f778fe
     def test_wait_fail(self):
f778fe
@@ -1108,7 +1161,14 @@ class Wait(CommonTest):
f778fe
             """
f778fe
         )
f778fe
         self.runner.set_runs(
f778fe
-            self.fixture_calls_initial() +
f778fe
+            fixture.call_wait_supported()
f778fe
+            +
f778fe
+            fixture.calls_cib(
f778fe
+                self.fixture_cib_pre,
f778fe
+                self.fixture_resources_bundle_simple,
f778fe
+                cib_base_file=self.cib_base_file,
f778fe
+            )
f778fe
+            +
f778fe
             fixture.call_wait(self.timeout, 62, fixture_wait_timeout_error)
f778fe
         )
f778fe
         assert_raise_library_error(
f778fe
@@ -1122,8 +1182,16 @@ class Wait(CommonTest):
f778fe
     @skip_unless_pacemaker_supports_bundle
f778fe
     def test_wait_ok_run_ok(self):
f778fe
         self.runner.set_runs(
f778fe
-            self.fixture_calls_initial() +
f778fe
-            fixture.call_wait(self.timeout) +
f778fe
+            fixture.call_wait_supported()
f778fe
+            +
f778fe
+            fixture.calls_cib(
f778fe
+                self.fixture_cib_pre,
f778fe
+                self.fixture_resources_bundle_simple,
f778fe
+                cib_base_file=self.cib_base_file,
f778fe
+            )
f778fe
+            +
f778fe
+            fixture.call_wait(self.timeout)
f778fe
+            +
f778fe
             fixture.call_status(fixture.state_complete(
f778fe
                 self.fixture_status_running
f778fe
             ))
f778fe
@@ -1139,8 +1207,16 @@ class Wait(CommonTest):
f778fe
     @skip_unless_pacemaker_supports_bundle
f778fe
     def test_wait_ok_run_fail(self):
f778fe
         self.runner.set_runs(
f778fe
-            self.fixture_calls_initial() +
f778fe
-            fixture.call_wait(self.timeout) +
f778fe
+            fixture.call_wait_supported()
f778fe
+            +
f778fe
+            fixture.calls_cib(
f778fe
+                self.fixture_cib_pre,
f778fe
+                self.fixture_resources_bundle_simple,
f778fe
+                cib_base_file=self.cib_base_file,
f778fe
+            )
f778fe
+            +
f778fe
+            fixture.call_wait(self.timeout)
f778fe
+            +
f778fe
             fixture.call_status(fixture.state_complete(
f778fe
                 self.fixture_status_not_running
f778fe
             ))
f778fe
@@ -1150,3 +1226,48 @@ class Wait(CommonTest):
f778fe
             fixture.report_resource_not_running("B1", severities.ERROR),
f778fe
         )
f778fe
         self.runner.assert_everything_launched()
f778fe
+
f778fe
+    @skip_unless_pacemaker_supports_bundle
f778fe
+    def test_disabled_wait_ok_run_ok(self):
f778fe
+        self.runner.set_runs(
f778fe
+            fixture.call_wait_supported()
f778fe
+            +
f778fe
+            fixture.calls_cib(
f778fe
+                self.fixture_cib_pre,
f778fe
+                self.fixture_resources_bundle_simple_disabled,
f778fe
+                cib_base_file=self.cib_base_file,
f778fe
+            )
f778fe
+            +
f778fe
+            fixture.call_wait(self.timeout)
f778fe
+            +
f778fe
+            fixture.call_status(fixture.state_complete(
f778fe
+                self.fixture_status_not_running
f778fe
+            ))
f778fe
+        )
f778fe
+        self.simple_bundle_create(self.timeout, disabled=True)
f778fe
+        self.runner.assert_everything_launched()
f778fe
+
f778fe
+    @skip_unless_pacemaker_supports_bundle
f778fe
+    def test_disabled_wait_ok_run_fail(self):
f778fe
+        self.runner.set_runs(
f778fe
+            fixture.call_wait_supported()
f778fe
+            +
f778fe
+            fixture.calls_cib(
f778fe
+                self.fixture_cib_pre,
f778fe
+                self.fixture_resources_bundle_simple_disabled,
f778fe
+                cib_base_file=self.cib_base_file,
f778fe
+            )
f778fe
+            +
f778fe
+            fixture.call_wait(self.timeout)
f778fe
+            +
f778fe
+            fixture.call_status(fixture.state_complete(
f778fe
+                self.fixture_status_running
f778fe
+            ))
f778fe
+        )
f778fe
+        assert_raise_library_error(
f778fe
+            lambda: self.simple_bundle_create(self.timeout, disabled=True),
f778fe
+            fixture.report_resource_running(
f778fe
+                "B1", {"Started": ["node1", "node2"]}, severities.ERROR
f778fe
+            )
f778fe
+        )
f778fe
+        self.runner.assert_everything_launched()
f778fe
diff --git a/pcs/lib/commands/test/resource/test_bundle_update.py b/pcs/lib/commands/test/resource/test_bundle_update.py
f778fe
index 55cfa7b..7a1ee49 100644
f778fe
--- a/pcs/lib/commands/test/resource/test_bundle_update.py
f778fe
+++ b/pcs/lib/commands/test/resource/test_bundle_update.py
f778fe
@@ -709,6 +709,96 @@ class StorageMap(CommonTest):
f778fe
         self.runner.assert_everything_launched()
f778fe
 
f778fe
 
f778fe
+class Meta(CommonTest):
f778fe
+    fixture_no_meta = """
f778fe
+        <resources>
f778fe
+            <bundle id="B1">
f778fe
+                <docker image="pcs:test" masters="3" replicas="6"/>
f778fe
+            </bundle>
f778fe
+        </resources>
f778fe
+    """
f778fe
+
f778fe
+    fixture_meta_stopped = """
f778fe
+        <resources>
f778fe
+            <bundle id="B1">
f778fe
+                <meta_attributes id="B1-meta_attributes">
f778fe
+                
f778fe
+                    name="target-role" value="Stopped" />
f778fe
+                </meta_attributes>
f778fe
+                <docker image="pcs:test" masters="3" replicas="6"/>
f778fe
+            </bundle>
f778fe
+        </resources>
f778fe
+    """
f778fe
+
f778fe
+    def test_add_meta_element(self):
f778fe
+        self.assert_command_effect(
f778fe
+            self.fixture_no_meta,
f778fe
+            lambda: resource.bundle_update(
f778fe
+                self.env, "B1",
f778fe
+                meta_attributes={
f778fe
+                    "target-role": "Stopped",
f778fe
+                }
f778fe
+            ),
f778fe
+            self.fixture_meta_stopped
f778fe
+        )
f778fe
+
f778fe
+    def test_remove_meta_element(self):
f778fe
+        self.assert_command_effect(
f778fe
+            self.fixture_meta_stopped,
f778fe
+            lambda: resource.bundle_update(
f778fe
+                self.env, "B1",
f778fe
+                meta_attributes={
f778fe
+                    "target-role": "",
f778fe
+                }
f778fe
+            ),
f778fe
+            self.fixture_no_meta
f778fe
+        )
f778fe
+
f778fe
+    def test_change_meta(self):
f778fe
+        fixture_cib_pre = """
f778fe
+            <resources>
f778fe
+                <bundle id="B1">
f778fe
+                    <meta_attributes id="B1-meta_attributes">
f778fe
+                    
f778fe
+                        name="target-role" value="Stopped" />
f778fe
+                    
f778fe
+                        name="priority" value="15" />
f778fe
+                    
f778fe
+                        name="is-managed" value="false" />
f778fe
+                    </meta_attributes>
f778fe
+                    <docker image="pcs:test" masters="3" replicas="6"/>
f778fe
+                </bundle>
f778fe
+            </resources>
f778fe
+        """
f778fe
+        fixture_cib_post = """
f778fe
+            <resources>
f778fe
+                <bundle id="B1">
f778fe
+                    <meta_attributes id="B1-meta_attributes">
f778fe
+                    
f778fe
+                        name="target-role" value="Stopped" />
f778fe
+                    
f778fe
+                        name="priority" value="10" />
f778fe
+                    
f778fe
+                        name="resource-stickiness" value="100" />
f778fe
+                    </meta_attributes>
f778fe
+                    <docker image="pcs:test" masters="3" replicas="6"/>
f778fe
+                </bundle>
f778fe
+            </resources>
f778fe
+        """
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_cib_pre,
f778fe
+            lambda: resource.bundle_update(
f778fe
+                self.env, "B1",
f778fe
+                meta_attributes={
f778fe
+                    "priority": "10",
f778fe
+                    "resource-stickiness": "100",
f778fe
+                    "is-managed": "",
f778fe
+                }
f778fe
+            ),
f778fe
+            fixture_cib_post
f778fe
+        )
f778fe
+
f778fe
+
f778fe
 class Wait(CommonTest):
f778fe
     fixture_status_running = """
f778fe
         <resources>
f778fe
@@ -794,7 +884,7 @@ class Wait(CommonTest):
f778fe
         self.runner.assert_everything_launched()
f778fe
 
f778fe
     @skip_unless_pacemaker_supports_bundle
f778fe
-    def test_wait_ok_run_ok(self):
f778fe
+    def test_wait_ok_running(self):
f778fe
         self.runner.set_runs(
f778fe
             self.fixture_calls_initial() +
f778fe
             fixture.call_wait(self.timeout) +
f778fe
@@ -811,7 +901,7 @@ class Wait(CommonTest):
f778fe
         self.runner.assert_everything_launched()
f778fe
 
f778fe
     @skip_unless_pacemaker_supports_bundle
f778fe
-    def test_wait_ok_run_fail(self):
f778fe
+    def test_wait_ok_not_running(self):
f778fe
         self.runner.set_runs(
f778fe
             self.fixture_calls_initial() +
f778fe
             fixture.call_wait(self.timeout) +
f778fe
@@ -819,8 +909,8 @@ class Wait(CommonTest):
f778fe
                 self.fixture_status_not_running
f778fe
             ))
f778fe
         )
f778fe
-        assert_raise_library_error(
f778fe
-            lambda: self.simple_bundle_update(self.timeout),
f778fe
-            fixture.report_resource_not_running("B1", severities.ERROR),
f778fe
-        )
f778fe
+        self.simple_bundle_update(self.timeout)
f778fe
+        self.env.report_processor.assert_reports([
f778fe
+            fixture.report_resource_not_running("B1", severities.INFO),
f778fe
+        ])
f778fe
         self.runner.assert_everything_launched()
f778fe
diff --git a/pcs/lib/commands/test/resource/test_resource_enable_disable.py b/pcs/lib/commands/test/resource/test_resource_enable_disable.py
f778fe
index 91ac068..b03740b 100644
f778fe
--- a/pcs/lib/commands/test/resource/test_resource_enable_disable.py
f778fe
+++ b/pcs/lib/commands/test/resource/test_resource_enable_disable.py
f778fe
@@ -469,6 +469,35 @@ fixture_bundle_cib_disabled_primitive = """
f778fe
         </bundle>
f778fe
     </resources>
f778fe
 """
f778fe
+fixture_bundle_cib_disabled_bundle = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <meta_attributes id="A-bundle-meta_attributes">
f778fe
+                
f778fe
+                    name="target-role" value="Stopped" />
f778fe
+            </meta_attributes>
f778fe
+            <docker image="pcs:test" />
f778fe
+            <primitive id="A" class="ocf" provider="heartbeat" type="Dummy" />
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
+fixture_bundle_cib_disabled_both = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <meta_attributes id="A-bundle-meta_attributes">
f778fe
+                
f778fe
+                    name="target-role" value="Stopped" />
f778fe
+            </meta_attributes>
f778fe
+            <docker image="pcs:test" />
f778fe
+            <primitive id="A" class="ocf" provider="heartbeat" type="Dummy">
f778fe
+                <meta_attributes id="A-meta_attributes">
f778fe
+                    
f778fe
+                        name="target-role" value="Stopped" />
f778fe
+                </meta_attributes>
f778fe
+            </primitive>
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
 fixture_bundle_status_managed = """
f778fe
     <resources>
f778fe
         
f778fe
@@ -486,7 +515,7 @@ fixture_bundle_status_managed = """
f778fe
 fixture_bundle_status_unmanaged = """
f778fe
     <resources>
f778fe
         
f778fe
-            unique="false" managed="true" failed="false"
f778fe
+            unique="false" managed="false" failed="false"
f778fe
         >
f778fe
             <replica id="0">
f778fe
                 <resource id="A" managed="false" />
f778fe
@@ -1460,17 +1489,12 @@ class DisableBundle(ResourceWithStateTest):
f778fe
         )
f778fe
 
f778fe
     def test_bundle(self):
f778fe
-        self.runner.set_runs(
f778fe
-            fixture.call_cib_load(
f778fe
-                fixture.cib_resources(fixture_bundle_cib_enabled)
f778fe
-            )
f778fe
-        )
f778fe
-
f778fe
-        assert_raise_library_error(
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_enabled,
f778fe
+            fixture_bundle_status_managed,
f778fe
             lambda: resource.disable(self.env, ["A-bundle"], False),
f778fe
-            fixture.report_not_for_bundles("A-bundle")
f778fe
+            fixture_bundle_cib_disabled_bundle
f778fe
         )
f778fe
-        self.runner.assert_everything_launched()
f778fe
 
f778fe
     def test_primitive_unmanaged(self):
f778fe
         self.assert_command_effect(
f778fe
@@ -1483,6 +1507,17 @@ class DisableBundle(ResourceWithStateTest):
f778fe
             ]
f778fe
         )
f778fe
 
f778fe
+    def test_bundle_unmanaged(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_enabled,
f778fe
+            fixture_bundle_status_unmanaged,
f778fe
+            lambda: resource.disable(self.env, ["A-bundle"], False),
f778fe
+            fixture_bundle_cib_disabled_bundle,
f778fe
+            reports=[
f778fe
+                fixture_report_unmanaged("A-bundle"),
f778fe
+            ]
f778fe
+        )
f778fe
+
f778fe
 
f778fe
 @skip_unless_pacemaker_supports_bundle
f778fe
 class EnableBundle(ResourceWithStateTest):
f778fe
@@ -1494,18 +1529,29 @@ class EnableBundle(ResourceWithStateTest):
f778fe
             fixture_bundle_cib_enabled
f778fe
         )
f778fe
 
f778fe
+    def test_primitive_disabled_both(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_disabled_both,
f778fe
+            fixture_bundle_status_managed,
f778fe
+            lambda: resource.enable(self.env, ["A"], False),
f778fe
+            fixture_bundle_cib_enabled
f778fe
+        )
f778fe
+
f778fe
     def test_bundle(self):
f778fe
-        self.runner.set_runs(
f778fe
-            fixture.call_cib_load(
f778fe
-                fixture.cib_resources(fixture_bundle_cib_enabled)
f778fe
-            )
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_disabled_bundle,
f778fe
+            fixture_bundle_status_managed,
f778fe
+            lambda: resource.enable(self.env, ["A-bundle"], False),
f778fe
+            fixture_bundle_cib_enabled
f778fe
         )
f778fe
 
f778fe
-        assert_raise_library_error(
f778fe
+    def test_bundle_disabled_both(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_disabled_both,
f778fe
+            fixture_bundle_status_managed,
f778fe
             lambda: resource.enable(self.env, ["A-bundle"], False),
f778fe
-            fixture.report_not_for_bundles("A-bundle")
f778fe
+            fixture_bundle_cib_enabled
f778fe
         )
f778fe
-        self.runner.assert_everything_launched()
f778fe
 
f778fe
     def test_primitive_unmanaged(self):
f778fe
         self.assert_command_effect(
f778fe
@@ -1515,5 +1561,18 @@ class EnableBundle(ResourceWithStateTest):
f778fe
             fixture_bundle_cib_enabled,
f778fe
             reports=[
f778fe
                 fixture_report_unmanaged("A"),
f778fe
+                fixture_report_unmanaged("A-bundle"),
f778fe
+            ]
f778fe
+        )
f778fe
+
f778fe
+    def test_bundle_unmanaged(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_disabled_primitive,
f778fe
+            fixture_bundle_status_unmanaged,
f778fe
+            lambda: resource.enable(self.env, ["A-bundle"], False),
f778fe
+            fixture_bundle_cib_enabled,
f778fe
+            reports=[
f778fe
+                fixture_report_unmanaged("A-bundle"),
f778fe
+                fixture_report_unmanaged("A"),
f778fe
             ]
f778fe
         )
f778fe
diff --git a/pcs/lib/commands/test/resource/test_resource_manage_unmanage.py b/pcs/lib/commands/test/resource/test_resource_manage_unmanage.py
f778fe
index 6d8c787..95b44bc 100644
f778fe
--- a/pcs/lib/commands/test/resource/test_resource_manage_unmanage.py
f778fe
+++ b/pcs/lib/commands/test/resource/test_resource_manage_unmanage.py
f778fe
@@ -517,6 +517,26 @@ fixture_clone_group_cib_unmanaged_all_primitives_op_disabled = """
f778fe
     </resources>
f778fe
 """
f778fe
 
f778fe
+
f778fe
+fixture_bundle_empty_cib_managed = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <docker image="pcs:test" />
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
+fixture_bundle_empty_cib_unmanaged_bundle = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <meta_attributes id="A-bundle-meta_attributes">
f778fe
+                
f778fe
+                    name="is-managed" value="false" />
f778fe
+            </meta_attributes>
f778fe
+            <docker image="pcs:test" />
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
+
f778fe
 fixture_bundle_cib_managed = """
f778fe
     <resources>
f778fe
         <bundle id="A-bundle">
f778fe
@@ -526,7 +546,19 @@ fixture_bundle_cib_managed = """
f778fe
         </bundle>
f778fe
     </resources>
f778fe
 """
f778fe
-
f778fe
+fixture_bundle_cib_unmanaged_bundle = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <meta_attributes id="A-bundle-meta_attributes">
f778fe
+                
f778fe
+                    name="is-managed" value="false" />
f778fe
+            </meta_attributes>
f778fe
+            <docker image="pcs:test" />
f778fe
+            <primitive id="A" class="ocf" provider="heartbeat" type="Dummy">
f778fe
+            </primitive>
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
 fixture_bundle_cib_unmanaged_primitive = """
f778fe
     <resources>
f778fe
         <bundle id="A-bundle">
f778fe
@@ -540,6 +572,78 @@ fixture_bundle_cib_unmanaged_primitive = """
f778fe
         </bundle>
f778fe
     </resources>
f778fe
 """
f778fe
+fixture_bundle_cib_unmanaged_both = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <meta_attributes id="A-bundle-meta_attributes">
f778fe
+                
f778fe
+                    name="is-managed" value="false" />
f778fe
+            </meta_attributes>
f778fe
+            <docker image="pcs:test" />
f778fe
+            <primitive id="A" class="ocf" provider="heartbeat" type="Dummy">
f778fe
+                <meta_attributes id="A-meta_attributes">
f778fe
+                    
f778fe
+                        name="is-managed" value="false" />
f778fe
+                </meta_attributes>
f778fe
+            </primitive>
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
+
f778fe
+fixture_bundle_cib_managed_op_enabled = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <docker image="pcs:test" />
f778fe
+            <primitive id="A" class="ocf" provider="heartbeat" type="Dummy">
f778fe
+                <operations>
f778fe
+                    <op id="A-start" name="start" />
f778fe
+                    <op id="A-stop" name="stop" />
f778fe
+                    <op id="A-monitor" name="monitor"/>
f778fe
+                </operations>
f778fe
+            </primitive>
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
+fixture_bundle_cib_unmanaged_primitive_op_disabled = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <docker image="pcs:test" />
f778fe
+            <primitive id="A" class="ocf" provider="heartbeat" type="Dummy">
f778fe
+                <meta_attributes id="A-meta_attributes">
f778fe
+                    
f778fe
+                        name="is-managed" value="false" />
f778fe
+                </meta_attributes>
f778fe
+                <operations>
f778fe
+                    <op id="A-start" name="start" />
f778fe
+                    <op id="A-stop" name="stop" />
f778fe
+                    <op id="A-monitor" name="monitor" enabled="false"/>
f778fe
+                </operations>
f778fe
+            </primitive>
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
+fixture_bundle_cib_unmanaged_both_op_disabled = """
f778fe
+    <resources>
f778fe
+        <bundle id="A-bundle">
f778fe
+            <meta_attributes id="A-bundle-meta_attributes">
f778fe
+                
f778fe
+                    name="is-managed" value="false" />
f778fe
+            </meta_attributes>
f778fe
+            <docker image="pcs:test" />
f778fe
+            <primitive id="A" class="ocf" provider="heartbeat" type="Dummy">
f778fe
+                <meta_attributes id="A-meta_attributes">
f778fe
+                    
f778fe
+                        name="is-managed" value="false" />
f778fe
+                </meta_attributes>
f778fe
+                <operations>
f778fe
+                    <op id="A-start" name="start" />
f778fe
+                    <op id="A-stop" name="stop" />
f778fe
+                    <op id="A-monitor" name="monitor" enabled="false"/>
f778fe
+                </operations>
f778fe
+            </primitive>
f778fe
+        </bundle>
f778fe
+    </resources>
f778fe
+"""
f778fe
 
f778fe
 def fixture_report_no_monitors(resource):
f778fe
     return (
f778fe
@@ -852,17 +956,18 @@ class UnmanageBundle(ResourceWithoutStateTest):
f778fe
         )
f778fe
 
f778fe
     def test_bundle(self):
f778fe
-        self.runner.set_runs(
f778fe
-            fixture.call_cib_load(
f778fe
-                fixture.cib_resources(fixture_bundle_cib_managed)
f778fe
-            )
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_managed,
f778fe
+            lambda: resource.unmanage(self.env, ["A-bundle"]),
f778fe
+            fixture_bundle_cib_unmanaged_both
f778fe
         )
f778fe
 
f778fe
-        assert_raise_library_error(
f778fe
-            lambda: resource.unmanage(self.env, ["A-bundle"], False),
f778fe
-            fixture.report_not_for_bundles("A-bundle")
f778fe
+    def test_bundle_empty(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_empty_cib_managed,
f778fe
+            lambda: resource.unmanage(self.env, ["A-bundle"]),
f778fe
+            fixture_bundle_empty_cib_unmanaged_bundle
f778fe
         )
f778fe
-        self.runner.assert_everything_launched()
f778fe
 
f778fe
 
f778fe
 class ManageBundle(ResourceWithoutStateTest):
f778fe
@@ -873,18 +978,47 @@ class ManageBundle(ResourceWithoutStateTest):
f778fe
             fixture_bundle_cib_managed,
f778fe
         )
f778fe
 
f778fe
+    def test_primitive_unmanaged_bundle(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_unmanaged_bundle,
f778fe
+            lambda: resource.manage(self.env, ["A"]),
f778fe
+            fixture_bundle_cib_managed,
f778fe
+        )
f778fe
+
f778fe
+    def test_primitive_unmanaged_both(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_unmanaged_both,
f778fe
+            lambda: resource.manage(self.env, ["A"]),
f778fe
+            fixture_bundle_cib_managed,
f778fe
+        )
f778fe
+
f778fe
     def test_bundle(self):
f778fe
-        self.runner.set_runs(
f778fe
-            fixture.call_cib_load(
f778fe
-                fixture.cib_resources(fixture_bundle_cib_unmanaged_primitive)
f778fe
-            )
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_unmanaged_bundle,
f778fe
+            lambda: resource.manage(self.env, ["A-bundle"]),
f778fe
+            fixture_bundle_cib_managed,
f778fe
         )
f778fe
 
f778fe
-        assert_raise_library_error(
f778fe
-            lambda: resource.manage(self.env, ["A-bundle"], False),
f778fe
-            fixture.report_not_for_bundles("A-bundle")
f778fe
+    def test_bundle_unmanaged_primitive(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_unmanaged_primitive,
f778fe
+            lambda: resource.manage(self.env, ["A-bundle"]),
f778fe
+            fixture_bundle_cib_managed,
f778fe
+        )
f778fe
+
f778fe
+    def test_bundle_unmanaged_both(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_unmanaged_both,
f778fe
+            lambda: resource.manage(self.env, ["A-bundle"]),
f778fe
+            fixture_bundle_cib_managed,
f778fe
+        )
f778fe
+
f778fe
+    def test_bundle_empty(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_empty_cib_unmanaged_bundle,
f778fe
+            lambda: resource.manage(self.env, ["A-bundle"]),
f778fe
+            fixture_bundle_empty_cib_managed
f778fe
         )
f778fe
-        self.runner.assert_everything_launched()
f778fe
 
f778fe
 
f778fe
 class MoreResources(ResourceWithoutStateTest):
f778fe
@@ -1090,3 +1224,24 @@ class WithMonitor(ResourceWithoutStateTest):
f778fe
             lambda: resource.unmanage(self.env, ["A1"], True),
f778fe
             fixture_clone_group_cib_unmanaged_primitive_op_disabled
f778fe
         )
f778fe
+
f778fe
+    def test_unmanage_bundle(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_managed_op_enabled,
f778fe
+            lambda: resource.unmanage(self.env, ["A-bundle"], True),
f778fe
+            fixture_bundle_cib_unmanaged_both_op_disabled
f778fe
+        )
f778fe
+
f778fe
+    def test_unmanage_in_bundle(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_cib_managed_op_enabled,
f778fe
+            lambda: resource.unmanage(self.env, ["A"], True),
f778fe
+            fixture_bundle_cib_unmanaged_primitive_op_disabled
f778fe
+        )
f778fe
+
f778fe
+    def test_unmanage_bundle_empty(self):
f778fe
+        self.assert_command_effect(
f778fe
+            fixture_bundle_empty_cib_managed,
f778fe
+            lambda: resource.unmanage(self.env, ["A-bundle"], True),
f778fe
+            fixture_bundle_empty_cib_unmanaged_bundle
f778fe
+        )
f778fe
diff --git a/pcs/lib/pacemaker/state.py b/pcs/lib/pacemaker/state.py
f778fe
index 71809db..be3e7ad 100644
f778fe
--- a/pcs/lib/pacemaker/state.py
f778fe
+++ b/pcs/lib/pacemaker/state.py
f778fe
@@ -201,6 +201,25 @@ def _get_primitive_roles_with_nodes(primitive_el_list):
f778fe
         for role, nodes in roles_with_nodes.items()
f778fe
     ])
f778fe
 
f778fe
+def info_resource_state(cluster_state, resource_id):
f778fe
+    roles_with_nodes = _get_primitive_roles_with_nodes(
f778fe
+        _get_primitives_for_state_check(
f778fe
+            cluster_state,
f778fe
+            resource_id,
f778fe
+            expected_running=True
f778fe
+        )
f778fe
+    )
f778fe
+    if not roles_with_nodes:
f778fe
+        return reports.resource_does_not_run(
f778fe
+            resource_id,
f778fe
+            severities.INFO
f778fe
+        )
f778fe
+    return reports.resource_running_on_nodes(
f778fe
+        resource_id,
f778fe
+        roles_with_nodes,
f778fe
+        severities.INFO
f778fe
+    )
f778fe
+
f778fe
 def ensure_resource_state(expected_running, cluster_state, resource_id):
f778fe
     roles_with_nodes = _get_primitive_roles_with_nodes(
f778fe
         _get_primitives_for_state_check(
f778fe
@@ -244,18 +263,25 @@ def is_resource_managed(cluster_state, resource_id):
f778fe
         for primitive in primitive_list:
f778fe
             if is_false(primitive.attrib.get("managed", "")):
f778fe
                 return False
f778fe
-            clone = find_parent(primitive, ["clone"])
f778fe
-            if clone is not None and is_false(clone.attrib.get("managed", "")):
f778fe
+            parent = find_parent(primitive, ["clone", "bundle"])
f778fe
+            if (
f778fe
+                parent is not None
f778fe
+                and
f778fe
+                is_false(parent.attrib.get("managed", ""))
f778fe
+            ):
f778fe
                 return False
f778fe
         return True
f778fe
 
f778fe
-    clone_list = cluster_state.xpath(
f778fe
-        """.//clone[@id="{0}"]""".format(resource_id)
f778fe
+    parent_list = cluster_state.xpath("""
f778fe
+        .//clone[@id="{0}"]
f778fe
+        |
f778fe
+        .//bundle[@id="{0}"]
f778fe
+        """.format(resource_id)
f778fe
     )
f778fe
-    for clone in clone_list:
f778fe
-        if is_false(clone.attrib.get("managed", "")):
f778fe
+    for parent in parent_list:
f778fe
+        if is_false(parent.attrib.get("managed", "")):
f778fe
             return False
f778fe
-        for primitive in clone.xpath(".//resource"):
f778fe
+        for primitive in parent.xpath(".//resource"):
f778fe
             if is_false(primitive.attrib.get("managed", "")):
f778fe
                 return False
f778fe
         return True
f778fe
diff --git a/pcs/lib/pacemaker/test/test_state.py b/pcs/lib/pacemaker/test/test_state.py
f778fe
index a29eddf..5de9426 100644
f778fe
--- a/pcs/lib/pacemaker/test/test_state.py
f778fe
+++ b/pcs/lib/pacemaker/test/test_state.py
f778fe
@@ -491,7 +491,7 @@ class GetPrimitivesForStateCheck(TestCase):
f778fe
         self.assert_primitives("B2-R2", ["B2-R2", "B2-R2"], False)
f778fe
 
f778fe
 
f778fe
-class EnsureResourceState(TestCase):
f778fe
+class CommonResourceState(TestCase):
f778fe
     resource_id = "R"
f778fe
     def setUp(self):
f778fe
         self.cluster_state = "state"
f778fe
@@ -526,6 +526,8 @@ class EnsureResourceState(TestCase):
f778fe
             "resource_id": self.resource_id
f778fe
         })
f778fe
 
f778fe
+
f778fe
+class EnsureResourceState(CommonResourceState):
f778fe
     def assert_running_info_transform(self, run_info, report, expected_running):
f778fe
         self.get_primitives_for_state_check.return_value = ["elem1", "elem2"]
f778fe
         self.get_primitive_roles_with_nodes.return_value = run_info
f778fe
@@ -575,6 +577,35 @@ class EnsureResourceState(TestCase):
f778fe
         )
f778fe
 
f778fe
 
f778fe
+class InfoResourceState(CommonResourceState):
f778fe
+    def assert_running_info_transform(self, run_info, report):
f778fe
+        self.get_primitives_for_state_check.return_value = ["elem1", "elem2"]
f778fe
+        self.get_primitive_roles_with_nodes.return_value = run_info
f778fe
+        assert_report_item_equal(
f778fe
+            state.info_resource_state(self.cluster_state, self.resource_id),
f778fe
+            report
f778fe
+        )
f778fe
+        self.get_primitives_for_state_check.assert_called_once_with(
f778fe
+            self.cluster_state,
f778fe
+            self.resource_id,
f778fe
+            expected_running=True
f778fe
+        )
f778fe
+        self.get_primitive_roles_with_nodes.assert_called_once_with(
f778fe
+            ["elem1", "elem2"]
f778fe
+        )
f778fe
+
f778fe
+    def test_report_info_running(self):
f778fe
+        self.assert_running_info_transform(
f778fe
+            self.fixture_running_state_info(),
f778fe
+            self.fixture_running_report(severities.INFO)
f778fe
+        )
f778fe
+    def test_report_info_not_running(self):
f778fe
+        self.assert_running_info_transform(
f778fe
+            [],
f778fe
+            self.fixture_not_running_report(severities.INFO)
f778fe
+        )
f778fe
+
f778fe
+
f778fe
 class IsResourceManaged(TestCase):
f778fe
     status_xml = etree.fromstring("""
f778fe
         <resources>
f778fe
@@ -733,6 +764,60 @@ class IsResourceManaged(TestCase):
f778fe
                     <resource id="R38:1" managed="false" />
f778fe
                 </group>
f778fe
             </clone>
f778fe
+
f778fe
+            <bundle id="B1" managed="true" />
f778fe
+            <bundle id="B2" managed="false" />
f778fe
+
f778fe
+            <bundle id="B3" managed="true">
f778fe
+                <replica id="0">
f778fe
+                    <resource id="R39" managed="true" />
f778fe
+                    <resource id="R40" managed="true" />
f778fe
+                </replica>
f778fe
+                <replica id="1">
f778fe
+                    <resource id="R39" managed="true" />
f778fe
+                    <resource id="R40" managed="true" />
f778fe
+                </replica>
f778fe
+            </bundle>
f778fe
+            <bundle id="B4" managed="false">
f778fe
+                <replica id="0">
f778fe
+                    <resource id="R41" managed="true" />
f778fe
+                    <resource id="R42" managed="true" />
f778fe
+                </replica>
f778fe
+                <replica id="1">
f778fe
+                    <resource id="R41" managed="true" />
f778fe
+                    <resource id="R42" managed="true" />
f778fe
+                </replica>
f778fe
+            </bundle>
f778fe
+            <bundle id="B5" managed="true">
f778fe
+                <replica id="0">
f778fe
+                    <resource id="R43" managed="false" />
f778fe
+                    <resource id="R44" managed="true" />
f778fe
+                </replica>
f778fe
+                <replica id="1">
f778fe
+                    <resource id="R43" managed="false" />
f778fe
+                    <resource id="R44" managed="true" />
f778fe
+                </replica>
f778fe
+            </bundle>
f778fe
+            <bundle id="B6" managed="true">
f778fe
+                <replica id="0">
f778fe
+                    <resource id="R45" managed="true" />
f778fe
+                    <resource id="R46" managed="false" />
f778fe
+                </replica>
f778fe
+                <replica id="1">
f778fe
+                    <resource id="R45" managed="true" />
f778fe
+                    <resource id="R46" managed="false" />
f778fe
+                </replica>
f778fe
+            </bundle>
f778fe
+            <bundle id="B7" managed="false">
f778fe
+                <replica id="0">
f778fe
+                    <resource id="R47" managed="false" />
f778fe
+                    <resource id="R48" managed="false" />
f778fe
+                </replica>
f778fe
+                <replica id="1">
f778fe
+                    <resource id="R47" managed="false" />
f778fe
+                    <resource id="R48" managed="false" />
f778fe
+                </replica>
f778fe
+            </bundle>
f778fe
         </resources>
f778fe
     """)
f778fe
 
f778fe
@@ -856,3 +941,24 @@ class IsResourceManaged(TestCase):
f778fe
         self.assert_managed("R36", False)
f778fe
         self.assert_managed("R37", False)
f778fe
         self.assert_managed("R38", False)
f778fe
+
f778fe
+    def test_bundle(self):
f778fe
+        self.assert_managed("B1", True)
f778fe
+        self.assert_managed("B2", False)
f778fe
+        self.assert_managed("B3", True)
f778fe
+        self.assert_managed("B4", False)
f778fe
+        self.assert_managed("B5", False)
f778fe
+        self.assert_managed("B6", False)
f778fe
+        self.assert_managed("B7", False)
f778fe
+
f778fe
+    def test_primitive_in_bundle(self):
f778fe
+        self.assert_managed("R39", True)
f778fe
+        self.assert_managed("R40", True)
f778fe
+        self.assert_managed("R41", False)
f778fe
+        self.assert_managed("R42", False)
f778fe
+        self.assert_managed("R43", False)
f778fe
+        self.assert_managed("R44", True)
f778fe
+        self.assert_managed("R45", True)
f778fe
+        self.assert_managed("R46", False)
f778fe
+        self.assert_managed("R47", False)
f778fe
+        self.assert_managed("R48", False)
f778fe
diff --git a/pcs/pcs.8 b/pcs/pcs.8
f778fe
index 446e7b3..20b5c2e 100644
f778fe
--- a/pcs/pcs.8
f778fe
+++ b/pcs/pcs.8
f778fe
@@ -162,10 +162,10 @@ Remove the clone which contains the specified group or resource (the resource or
f778fe
 master [<master/slave id>] <resource id | group id> [options] [\fB\-\-wait\fR[=n]]
f778fe
 Configure a resource or group as a multi\-state (master/slave) resource.  If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the operation to finish (including starting and promoting resource instances if appropriate) and then return 0 on success or 1 on error.  If 'n' is not specified it defaults to 60 minutes.  Note: to remove a master you must remove the resource/group it contains.
f778fe
 .TP
f778fe
-bundle create <bundle id> [container [<container type>] <container options>] [network <network options>] [port\-map <port options>]... [storage\-map <storage options>]... [\fB\-\-wait\fR[=n]]
f778fe
-Create a new bundle encapsulating no resources. The bundle can be used either as it is or a resource may be put into it at any time. If the container type is not specified, it defaults to 'docker'.  If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the bundle to start and then return 0 on success or 1 on error.  If 'n' is not specified it defaults to 60 minutes.
f778fe
+bundle create <bundle id> [container [<container type>] <container options>] [network <network options>] [port\-map <port options>]... [storage\-map <storage options>]... [meta <meta options>] [\fB\-\-disabled\fR] [\fB\-\-wait\fR[=n]]
f778fe
+Create a new bundle encapsulating no resources. The bundle can be used either as it is or a resource may be put into it at any time. If the container type is not specified, it defaults to 'docker'. If \fB\-\-disabled\fR is specified, the bundle is not started automatically. If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the bundle to start and then return 0 on success or 1 on error. If 'n' is not specified it defaults to 60 minutes.
f778fe
 .TP
f778fe
-bundle update <bundle id> [container <container options>] [network <network options>] [port\-map (add <port options>) | (remove <id>...)]... [storage\-map (add <storage options>) | (remove <id>...)]... [\fB\-\-wait\fR[=n]]
f778fe
+bundle update <bundle id> [container <container options>] [network <network options>] [port\-map (add <port options>) | (remove <id>...)]... [storage\-map (add <storage options>) | (remove <id>...)]... [meta <meta options>] [\fB\-\-wait\fR[=n]]
f778fe
 Add, remove or change options to specified bundle. If you wish to update a resource encapsulated in the bundle, use the 'pcs resource update' command instead and specify the resource id.  If \fB\-\-wait\fR is specified, pcs will wait up to 'n' seconds for the operation to finish (including moving resources if appropriate) and then return 0 on success or 1 on error.  If 'n' is not specified it defaults to 60 minutes.
f778fe
 .TP
f778fe
 manage <resource id>... [\fB\-\-monitor\fR]
f778fe
diff --git a/pcs/resource.py b/pcs/resource.py
f778fe
index dc6da13..467faa5 100644
f778fe
--- a/pcs/resource.py
f778fe
+++ b/pcs/resource.py
f778fe
@@ -20,7 +20,7 @@ from pcs import (
f778fe
 )
f778fe
 from pcs.settings import pacemaker_wait_timeout_status as \
f778fe
     PACEMAKER_WAIT_TIMEOUT_STATUS
f778fe
-import pcs.lib.cib.acl as lib_acl
f778fe
+from pcs.cli.common.console_report import error, warn
f778fe
 from pcs.cli.common.errors import CmdLineInputError
f778fe
 from pcs.cli.common.parse_args import prepare_options
f778fe
 from pcs.cli.resource.parse_args import (
f778fe
@@ -28,16 +28,21 @@ from pcs.cli.resource.parse_args import (
f778fe
     parse_bundle_update_options,
f778fe
     parse_create as parse_create_args,
f778fe
 )
f778fe
-from pcs.lib.errors import LibraryError
f778fe
+import pcs.lib.cib.acl as lib_acl
f778fe
 from pcs.lib.cib.resource import guest_node
f778fe
-import pcs.lib.pacemaker.live as lib_pacemaker
f778fe
-from pcs.lib.pacemaker.values import timeout_to_seconds
f778fe
-import pcs.lib.resource_agent as lib_ra
f778fe
-from pcs.cli.common.console_report import error, warn
f778fe
 from pcs.lib.commands.resource import(
f778fe
     _validate_guest_change,
f778fe
     _get_nodes_to_validate_against,
f778fe
 )
f778fe
+from pcs.lib.errors import LibraryError
f778fe
+import pcs.lib.pacemaker.live as lib_pacemaker
f778fe
+from pcs.lib.pacemaker.state import (
f778fe
+    get_cluster_state_dom,
f778fe
+    _get_primitive_roles_with_nodes,
f778fe
+    _get_primitives_for_state_check,
f778fe
+)
f778fe
+from pcs.lib.pacemaker.values import timeout_to_seconds
f778fe
+import pcs.lib.resource_agent as lib_ra
f778fe
 
f778fe
 
f778fe
 RESOURCE_RELOCATE_CONSTRAINT_PREFIX = "pcs-relocate-"
f778fe
@@ -1432,6 +1437,18 @@ def resource_master_create(dom, argv, update=False, master_id=None):
f778fe
     return dom, master_element.getAttribute("id")
f778fe
 
f778fe
 def resource_remove(resource_id, output=True, is_remove_remote_context=False):
f778fe
+    def is_bundle_running(bundle_id):
f778fe
+        roles_with_nodes = _get_primitive_roles_with_nodes(
f778fe
+            _get_primitives_for_state_check(
f778fe
+                get_cluster_state_dom(
f778fe
+                    lib_pacemaker.get_cluster_status_xml(utils.cmd_runner())
f778fe
+                ),
f778fe
+                bundle_id,
f778fe
+                expected_running=True
f778fe
+            )
f778fe
+        )
f778fe
+        return True if roles_with_nodes else False
f778fe
+
f778fe
     dom = utils.get_cib_dom()
f778fe
     # if resource is a clone or a master, work with its child instead
f778fe
     cloned_resource = utils.dom_get_clone_ms_resource(dom, resource_id)
f778fe
@@ -1441,6 +1458,40 @@ def resource_remove(resource_id, output=True, is_remove_remote_context=False):
f778fe
     bundle = utils.dom_get_bundle(dom, resource_id)
f778fe
     if bundle is not None:
f778fe
         primitive_el = utils.dom_get_resource_bundle(bundle)
f778fe
+        if primitive_el is None:
f778fe
+            print("Deleting bundle '{0}'".format(resource_id))
f778fe
+        else:
f778fe
+            print(
f778fe
+                "Deleting bundle '{0}' and its inner resource '{1}'".format(
f778fe
+                    resource_id,
f778fe
+                    primitive_el.getAttribute("id")
f778fe
+                )
f778fe
+            )
f778fe
+
f778fe
+        if (
f778fe
+            "--force" not in utils.pcs_options
f778fe
+            and
f778fe
+            not utils.usefile
f778fe
+            and
f778fe
+            is_bundle_running(resource_id)
f778fe
+        ):
f778fe
+            sys.stdout.write("Stopping bundle '{0}'... ".format(resource_id))
f778fe
+            sys.stdout.flush()
f778fe
+            lib = utils.get_library_wrapper()
f778fe
+            lib.resource.disable([resource_id], False)
f778fe
+            output, retval = utils.run(["crm_resource", "--wait"])
f778fe
+            # pacemaker which supports bundles supports --wait as well
f778fe
+            if is_bundle_running(resource_id):
f778fe
+                msg = [
f778fe
+                    "Unable to stop: %s before deleting "
f778fe
+                    "(re-run with --force to force deletion)"
f778fe
+                    % resource_id
f778fe
+                ]
f778fe
+                if retval != 0 and output:
f778fe
+                    msg.append("\n" + output)
f778fe
+                utils.err("\n".join(msg).strip())
f778fe
+            print("Stopped")
f778fe
+
f778fe
         if primitive_el is not None:
f778fe
             resource_remove(primitive_el.getAttribute("id"))
f778fe
         utils.replace_cib_configuration(
f778fe
@@ -1498,7 +1549,7 @@ def resource_remove(resource_id, output=True, is_remove_remote_context=False):
f778fe
             resource_remove(res.getAttribute("id"))
f778fe
         sys.exit(0)
f778fe
 
f778fe
-    # now we know resource is not a group, a clone nor a master
f778fe
+    # now we know resource is not a group, a clone, a master nor a bundle
f778fe
     # because of the conditions above
f778fe
     if not utils.does_exist('//resources/descendant::primitive[@id="'+resource_id+'"]'):
f778fe
         utils.err("Resource '{0}' does not exist.".format(resource_id))
f778fe
@@ -1517,7 +1568,7 @@ def resource_remove(resource_id, output=True, is_remove_remote_context=False):
f778fe
         and
f778fe
         utils.resource_running_on(resource_id)["is_running"]
f778fe
     ):
f778fe
-        sys.stdout.write("Attempting to stop: "+ resource_id + "...")
f778fe
+        sys.stdout.write("Attempting to stop: "+ resource_id + "... ")
f778fe
         sys.stdout.flush()
f778fe
         lib = utils.get_library_wrapper()
f778fe
         # we are not using wait from disable command, because if wait is not
f778fe
@@ -2246,6 +2297,7 @@ def print_node(node, tab = 0):
f778fe
             node.findall("storage/storage-mapping"),
f778fe
             spaces + " "
f778fe
         )
f778fe
+        print_meta_vars_string(node, spaces)
f778fe
         for child in node:
f778fe
             print_node(child, tab + 1)
f778fe
         return
f778fe
@@ -2675,12 +2727,14 @@ def resource_bundle_create_cmd(lib, argv, modifiers):
f778fe
     lib.resource.bundle_create(
f778fe
         bundle_id,
f778fe
         parts["container_type"],
f778fe
-        parts["container"],
f778fe
-        parts["network"],
f778fe
-        parts["port_map"],
f778fe
-        parts["storage_map"],
f778fe
-        modifiers["force"],
f778fe
-        modifiers["wait"]
f778fe
+        container_options=parts["container"],
f778fe
+        network_options=parts["network"],
f778fe
+        port_map=parts["port_map"],
f778fe
+        storage_map=parts["storage_map"],
f778fe
+        meta_attributes=parts["meta"],
f778fe
+        force_options=modifiers["force"],
f778fe
+        ensure_disabled=modifiers["disabled"],
f778fe
+        wait=modifiers["wait"]
f778fe
     )
f778fe
 
f778fe
 def resource_bundle_update_cmd(lib, argv, modifiers):
f778fe
@@ -2691,12 +2745,13 @@ def resource_bundle_update_cmd(lib, argv, modifiers):
f778fe
     parts = parse_bundle_update_options(argv[1:])
f778fe
     lib.resource.bundle_update(
f778fe
         bundle_id,
f778fe
-        parts["container"],
f778fe
-        parts["network"],
f778fe
-        parts["port_map_add"],
f778fe
-        parts["port_map_remove"],
f778fe
-        parts["storage_map_add"],
f778fe
-        parts["storage_map_remove"],
f778fe
-        modifiers["force"],
f778fe
-        modifiers["wait"]
f778fe
+        container_options=parts["container"],
f778fe
+        network_options=parts["network"],
f778fe
+        port_map_add=parts["port_map_add"],
f778fe
+        port_map_remove=parts["port_map_remove"],
f778fe
+        storage_map_add=parts["storage_map_add"],
f778fe
+        storage_map_remove=parts["storage_map_remove"],
f778fe
+        meta_attributes=parts["meta"],
f778fe
+        force_options=modifiers["force"],
f778fe
+        wait=modifiers["wait"]
f778fe
     )
f778fe
diff --git a/pcs/test/cib_resource/test_bundle.py b/pcs/test/cib_resource/test_bundle.py
f778fe
index d8c97c6..29e4339 100644
f778fe
--- a/pcs/test/cib_resource/test_bundle.py
f778fe
+++ b/pcs/test/cib_resource/test_bundle.py
f778fe
@@ -75,6 +75,7 @@ class BundleCreate(BundleCreateCommon):
f778fe
                 resource bundle create B1
f778fe
                 container replicas=4 replicas-per-host=2 run-command=/bin/true
f778fe
                 port-map port=1001
f778fe
+                meta target-role=Stopped
f778fe
                 network control-port=12345 host-interface=eth0 host-netmask=24
f778fe
                 port-map id=B1-port-map-1001 internal-port=2002 port=2000
f778fe
                 port-map range=3000-3300
f778fe
@@ -83,6 +84,7 @@ class BundleCreate(BundleCreateCommon):
f778fe
                 storage-map id=B1-storage-map source-dir=/tmp/docker2a
f778fe
                     target-dir=/tmp/docker2b
f778fe
                 container image=pcs:test masters=0
f778fe
+                meta is-managed=false
f778fe
                 storage-map source-dir-root=/tmp/docker3a
f778fe
                     target-dir=/tmp/docker3b
f778fe
                 storage-map id=B1-port-map-1001-1 source-dir-root=/tmp/docker4a
f778fe
@@ -140,6 +142,18 @@ class BundleCreate(BundleCreateCommon):
f778fe
                                 target-dir="/tmp/docker4b"
f778fe
                             />
f778fe
                         </storage>
f778fe
+                        <meta_attributes id="B1-meta_attributes">
f778fe
+                            
f778fe
+                                id="B1-meta_attributes-is-managed"
f778fe
+                                name="is-managed"
f778fe
+                                value="false"
f778fe
+                            />
f778fe
+                            
f778fe
+                                id="B1-meta_attributes-target-role"
f778fe
+                                name="target-role"
f778fe
+                                value="Stopped"
f778fe
+                            />
f778fe
+                        </meta_attributes>
f778fe
                     </bundle>
f778fe
                 </resources>
f778fe
             """
f778fe
@@ -215,6 +229,9 @@ class BundleCreate(BundleCreateCommon):
f778fe
     def test_empty_port_map(self):
f778fe
         self.assert_no_options("port-map")
f778fe
 
f778fe
+    def test_empty_meta(self):
f778fe
+        self.assert_no_options("meta")
f778fe
+
f778fe
 
f778fe
 @skip_unless_pacemaker_supports_bundle
f778fe
 class BundleUpdate(BundleCreateCommon):
f778fe
@@ -239,6 +256,7 @@ class BundleUpdate(BundleCreateCommon):
f778fe
                 "storage-map source-dir=/tmp/docker1a target-dir=/tmp/docker1b "
f778fe
                 "storage-map source-dir=/tmp/docker2a target-dir=/tmp/docker2b "
f778fe
                 "storage-map source-dir=/tmp/docker3a target-dir=/tmp/docker3b "
f778fe
+                "meta priority=15 resource-stickiness=100 is-managed=false "
f778fe
             ).format(name)
f778fe
         )
f778fe
 
f778fe
@@ -282,6 +300,7 @@ class BundleUpdate(BundleCreateCommon):
f778fe
                 port-map add internal-port=1003 port=2003
f778fe
                 storage-map remove B-storage-map B-storage-map-2
f778fe
                 storage-map add source-dir=/tmp/docker4a target-dir=/tmp/docker4b
f778fe
+                meta priority=10 is-managed= target-role=Stopped
f778fe
             """,
f778fe
             """
f778fe
                 <resources>
f778fe
@@ -319,6 +338,14 @@ class BundleUpdate(BundleCreateCommon):
f778fe
                                 target-dir="/tmp/docker4b"
f778fe
                             />
f778fe
                         </storage>
f778fe
+                        <meta_attributes id="B-meta_attributes">
f778fe
+                            
f778fe
+                                name="priority" value="10" />
f778fe
+                            
f778fe
+                                name="resource-stickiness" value="100" />
f778fe
+                            
f778fe
+                                name="target-role" value="Stopped" />
f778fe
+                        </meta_attributes>
f778fe
                     </bundle>
f778fe
                 </resources>
f778fe
             """
f778fe
@@ -373,6 +400,9 @@ class BundleUpdate(BundleCreateCommon):
f778fe
     def test_empty_port_map(self):
f778fe
         self.assert_no_options("port-map")
f778fe
 
f778fe
+    def test_empty_meta(self):
f778fe
+        self.assert_no_options("meta")
f778fe
+
f778fe
 
f778fe
 @skip_unless_pacemaker_supports_bundle
f778fe
 class BundleShow(TestCase, AssertPcsMixin):
f778fe
@@ -463,6 +493,35 @@ class BundleShow(TestCase, AssertPcsMixin):
f778fe
             """
f778fe
         ))
f778fe
 
f778fe
+    def test_meta(self):
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource bundle create B1 container image=pcs:test --disabled"
f778fe
+        )
f778fe
+        self.assert_pcs_success("resource show B1", outdent(
f778fe
+            # pylint:disable=trailing-whitespace
f778fe
+            """\
f778fe
+             Bundle: B1
f778fe
+              Docker: image=pcs:test
f778fe
+              Meta Attrs: target-role=Stopped 
f778fe
+            """
f778fe
+        ))
f778fe
+
f778fe
+    def test_resource(self):
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource bundle create B1 container image=pcs:test"
f778fe
+        )
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource create A ocf:pacemaker:Dummy bundle B1 --no-default-ops"
f778fe
+        )
f778fe
+        self.assert_pcs_success("resource show B1", outdent(
f778fe
+            """\
f778fe
+             Bundle: B1
f778fe
+              Docker: image=pcs:test
f778fe
+              Resource: A (class=ocf provider=pacemaker type=Dummy)
f778fe
+               Operations: monitor interval=10 timeout=20 (A-monitor-interval-10)
f778fe
+            """
f778fe
+        ))
f778fe
+
f778fe
     def test_all(self):
f778fe
         self.assert_pcs_success(
f778fe
             """
f778fe
@@ -474,9 +533,14 @@ class BundleShow(TestCase, AssertPcsMixin):
f778fe
                 storage-map source-dir=/tmp/docker1a target-dir=/tmp/docker1b
f778fe
                 storage-map id=my-storage-map source-dir=/tmp/docker2a
f778fe
                     target-dir=/tmp/docker2b
f778fe
+                meta target-role=Stopped is-managed=false
f778fe
             """
f778fe
         )
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource create A ocf:pacemaker:Dummy bundle B1 --no-default-ops"
f778fe
+        )
f778fe
         self.assert_pcs_success("resource show B1", outdent(
f778fe
+            # pylint:disable=trailing-whitespace
f778fe
             """\
f778fe
              Bundle: B1
f778fe
               Docker: image=pcs:test masters=2 options="a b c" replicas=4
f778fe
@@ -487,5 +551,8 @@ class BundleShow(TestCase, AssertPcsMixin):
f778fe
               Storage Mapping:
f778fe
                source-dir=/tmp/docker1a target-dir=/tmp/docker1b (B1-storage-map)
f778fe
                source-dir=/tmp/docker2a target-dir=/tmp/docker2b (my-storage-map)
f778fe
+              Meta Attrs: is-managed=false target-role=Stopped 
f778fe
+              Resource: A (class=ocf provider=pacemaker type=Dummy)
f778fe
+               Operations: monitor interval=10 timeout=20 (A-monitor-interval-10)
f778fe
             """
f778fe
         ))
f778fe
diff --git a/pcs/test/cib_resource/test_manage_unmanage.py b/pcs/test/cib_resource/test_manage_unmanage.py
f778fe
index 5b78646..2a87cd3 100644
f778fe
--- a/pcs/test/cib_resource/test_manage_unmanage.py
f778fe
+++ b/pcs/test/cib_resource/test_manage_unmanage.py
f778fe
@@ -18,6 +18,7 @@ class ManageUnmanage(
f778fe
     TestCase,
f778fe
     get_assert_pcs_effect_mixin(
f778fe
         lambda cib: etree.tostring(
f778fe
+            # pylint:disable=undefined-variable
f778fe
             etree.parse(cib).findall(".//resources")[0]
f778fe
         )
f778fe
     )
f778fe
@@ -234,7 +235,7 @@ class ManageUnmanage(
f778fe
 
f778fe
         self.assert_pcs_fail(
f778fe
             "resource unmanage A B",
f778fe
-            "Error: resource/clone/master/group 'B' does not exist\n"
f778fe
+            "Error: resource/clone/master/group/bundle 'B' does not exist\n"
f778fe
         )
f778fe
         self.assert_resources_xml_in_cib(
f778fe
             """
f778fe
@@ -255,7 +256,7 @@ class ManageUnmanage(
f778fe
 
f778fe
         self.assert_pcs_fail(
f778fe
             "resource manage A B",
f778fe
-            "Error: resource/clone/master/group 'B' does not exist\n"
f778fe
+            "Error: resource/clone/master/group/bundle 'B' does not exist\n"
f778fe
         )
f778fe
         self.assert_resources_xml_in_cib(
f778fe
             """
f778fe
diff --git a/pcs/test/test_resource.py b/pcs/test/test_resource.py
f778fe
index 96eae8f..4bdc194 100644
f778fe
--- a/pcs/test/test_resource.py
f778fe
+++ b/pcs/test/test_resource.py
f778fe
@@ -8,6 +8,7 @@ from __future__ import (
f778fe
 from lxml import etree
f778fe
 import re
f778fe
 import shutil
f778fe
+from textwrap import dedent
f778fe
 
f778fe
 from pcs.test.tools import pcs_unittest as unittest
f778fe
 from pcs.test.tools.assertions import AssertPcsMixin
f778fe
@@ -3321,11 +3322,11 @@ Error: Cannot remove more than one resource from cloned group
f778fe
 
f778fe
         # bad resource name
f778fe
         o,r = pcs(temp_cib, "resource enable NoExist")
f778fe
-        ac(o,"Error: resource/clone/master/group 'NoExist' does not exist\n")
f778fe
+        ac(o,"Error: resource/clone/master/group/bundle 'NoExist' does not exist\n")
f778fe
         assert r == 1
f778fe
 
f778fe
         o,r = pcs(temp_cib, "resource disable NoExist")
f778fe
-        ac(o,"Error: resource/clone/master/group 'NoExist' does not exist\n")
f778fe
+        ac(o,"Error: resource/clone/master/group/bundle 'NoExist' does not exist\n")
f778fe
         assert r == 1
f778fe
 
f778fe
         # cloned group
f778fe
@@ -3829,7 +3830,7 @@ Error: Cannot remove more than one resource from cloned group
f778fe
 
f778fe
         self.assert_pcs_fail_regardless_of_force(
f778fe
             "resource enable dummy3 dummyX",
f778fe
-            "Error: resource/clone/master/group 'dummyX' does not exist\n"
f778fe
+            "Error: resource/clone/master/group/bundle 'dummyX' does not exist\n"
f778fe
         )
f778fe
         self.assert_pcs_success(
f778fe
             "resource show --full",
f778fe
@@ -3849,7 +3850,7 @@ Error: Cannot remove more than one resource from cloned group
f778fe
 
f778fe
         self.assert_pcs_fail_regardless_of_force(
f778fe
             "resource disable dummy1 dummyX",
f778fe
-            "Error: resource/clone/master/group 'dummyX' does not exist\n"
f778fe
+            "Error: resource/clone/master/group/bundle 'dummyX' does not exist\n"
f778fe
         )
f778fe
         self.assert_pcs_success(
f778fe
             "resource show --full",
f778fe
@@ -4719,7 +4720,11 @@ class BundleCommon(
f778fe
 class BundleDeleteTest(BundleCommon):
f778fe
     def test_without_primitive(self):
f778fe
         self.fixture_bundle("B")
f778fe
-        self.assert_effect("resource delete B", "<resources/>")
f778fe
+        self.assert_effect(
f778fe
+            "resource delete B",
f778fe
+            "<resources/>",
f778fe
+            "Deleting bundle 'B'\n"
f778fe
+        )
f778fe
 
f778fe
     def test_with_primitive(self):
f778fe
         self.fixture_bundle("B")
f778fe
@@ -4727,7 +4732,10 @@ class BundleDeleteTest(BundleCommon):
f778fe
         self.assert_effect(
f778fe
             "resource delete B",
f778fe
             "<resources/>",
f778fe
-            "Deleting Resource - R\n",
f778fe
+            dedent("""\
f778fe
+                Deleting bundle 'B' and its inner resource 'R'
f778fe
+                Deleting Resource - R
f778fe
+            """),
f778fe
         )
f778fe
 
f778fe
     def test_remove_primitive(self):
f778fe
@@ -4823,30 +4831,26 @@ class BundleCloneMaster(BundleCommon):
f778fe
 class BundleMiscCommands(BundleCommon):
f778fe
     def test_resource_enable_bundle(self):
f778fe
         self.fixture_bundle("B")
f778fe
-        self.assert_pcs_fail_regardless_of_force(
f778fe
-            "resource enable B",
f778fe
-            "Error: 'B' is not clone/master/a group/primitive\n"
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource enable B"
f778fe
         )
f778fe
 
f778fe
     def test_resource_disable_bundle(self):
f778fe
         self.fixture_bundle("B")
f778fe
-        self.assert_pcs_fail_regardless_of_force(
f778fe
-            "resource disable B",
f778fe
-            "Error: 'B' is not clone/master/a group/primitive\n"
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource disable B"
f778fe
         )
f778fe
 
f778fe
     def test_resource_manage_bundle(self):
f778fe
         self.fixture_bundle("B")
f778fe
-        self.assert_pcs_fail_regardless_of_force(
f778fe
-            "resource manage B",
f778fe
-            "Error: 'B' is not clone/master/a group/primitive\n"
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource manage B"
f778fe
         )
f778fe
 
f778fe
     def test_resource_unmanage_bundle(self):
f778fe
         self.fixture_bundle("B")
f778fe
-        self.assert_pcs_fail_regardless_of_force(
f778fe
-            "resource unmanage B",
f778fe
-            "Error: 'B' is not clone/master/a group/primitive\n"
f778fe
+        self.assert_pcs_success(
f778fe
+            "resource unmanage B"
f778fe
         )
f778fe
 
f778fe
     def test_op_add(self):
f778fe
diff --git a/pcs/usage.py b/pcs/usage.py
f778fe
index d2262a6..75cb118 100644
f778fe
--- a/pcs/usage.py
f778fe
+++ b/pcs/usage.py
f778fe
@@ -430,10 +430,12 @@ Commands:
f778fe
 
f778fe
     bundle create <bundle id> [container [<container type>] <container options>]
f778fe
             [network <network options>] [port-map <port options>]...
f778fe
-            [storage-map <storage options>]... [--wait[=n]]
f778fe
+            [storage-map <storage options>]... [meta <meta options>]
f778fe
+            [--disabled] [--wait[=n]]
f778fe
         Create a new bundle encapsulating no resources. The bundle can be used
f778fe
         either as it is or a resource may be put into it at any time.
f778fe
         If the container type is not specified, it defaults to 'docker'.
f778fe
+        If --disabled is specified, the bundle is not started automatically.
f778fe
         If --wait is specified, pcs will wait up to 'n' seconds for the bundle
f778fe
         to start and then return 0 on success or 1 on error. If 'n' is not
f778fe
         specified it defaults to 60 minutes.
f778fe
@@ -442,13 +444,14 @@ Commands:
f778fe
             [network <network options>]
f778fe
             [port-map (add <port options>) | (remove <id>...)]...
f778fe
             [storage-map (add <storage options>) | (remove <id>...)]...
f778fe
+            [meta <meta options>]
f778fe
             [--wait[=n]]
f778fe
         Add, remove or change options to specified bundle. If you wish to update
f778fe
         a resource encapsulated in the bundle, use the 'pcs resource update'
f778fe
-        command instead and specify the resource id.  If --wait is specified,
f778fe
+        command instead and specify the resource id. If --wait is specified,
f778fe
         pcs will wait up to 'n' seconds for the operation to finish (including
f778fe
         moving resources if appropriate) and then return 0 on success or 1 on
f778fe
-        error.  If 'n' is not specified it defaults to 60 minutes.
f778fe
+        error. If 'n' is not specified it defaults to 60 minutes.
f778fe
 
f778fe
     manage <resource id>... [--monitor]
f778fe
         Set resources listed to managed mode (default). If --monitor is
f778fe
-- 
f778fe
1.8.3.1
f778fe