Add api and manager states and metadata

* api state configures and starts octavia-api service
* manager state configures and starts the following services:
  - octavia-worker
  - octavia-health-manager
  - octavia-housekeeping

Added tests and updated the README.rst

Change-Id: I41c3097ff400b07d659d8aa845d295fbda480cd3
diff --git a/.kitchen.yml b/.kitchen.yml
index c061a66..cc263dd 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -15,36 +15,47 @@
   grains:
     noservices: True
   dependencies:
-    - name: neutron
+    - name: linux
       repo: git
-      source: https://github.com/salt-formulas/salt-formula-neutron
-    - name: nova
-      repo: git
-      source: https://github.com/salt-formulas/salt-formula-nova
+      source: https://github.com/salt-formulas/salt-formula-linux
   state_top:
     base:
       "*":
+        - linux.system
         - octavia
   pillars:
     top.sls:
       base:
         "*":
+          - linux_repo_openstack
           - octavia
+  pillars-from-files:
+    linux_repo_openstack.sls: tests/pillar/repo_openstack.sls
 
 verifier:
   name: inspec
   sudo: true
 
-
 platforms:
-  - name: <%=ENV['PLATFORM'] || 'ubuntu-xenial'%>
+  - name: 'ubuntu-xenial'
     driver_config:
-      image: <%=ENV['PLATFORM'] || 'trevorj/salty-whales:xenial'%>
+      image: 'trevorj/salty-whales:xenial'
       platform: ubuntu
 
 suites:
-  - name: single
+  - name: api_single
     provisioner:
       pillars-from-files:
-        octavia.sls: tests/pillar/single.sls
+        octavia.sls: tests/pillar/api_single.sls
+
+  - name: api_cluster
+    provisioner:
+      pillars-from-files:
+        octavia.sls: tests/pillar/api_cluster.sls
+
+  - name: manager_single
+    provisioner:
+      pillars-from-files:
+        octavia.sls: tests/pillar/manager_single.sls
+
 # vim: ft=yaml sw=2 ts=2 sts=2 tw=125
diff --git a/.travis.yml b/.travis.yml
index 7a77247..18742e0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,7 +17,6 @@
   - bundle install
 
 env:
-    - PLATFORM=trevorj/salty-whales:trusty
     - PLATFORM=trevorj/salty-whales:xenial
 
 before_script:
diff --git a/README.rst b/README.rst
index 8790ce0..169e463 100644
--- a/README.rst
+++ b/README.rst
@@ -2,16 +2,112 @@
 Octavia
 =======
 
-Install and configure Octavia.
+Octavia is an open source, operator-scale load balancing solution designed to
+work with OpenStack. It accomplishes its delivery of load balancing services
+by managing a fleet of virtual machines, known as amphorae, which it spins up
+on demand.
+
+Octavia is designed to “plug in” to Neutron LBaaS in the same way that any
+proprietary vendor solution would: through a Neutron LBaaS version 2 driver
+interface. Octavia plans to supplant Neutron LBaaS as the load balancing
+solution for OpenStack. At that time, third-party vendor drivers that presently
+“plug in” to Neutron LBaaS will plug in to Octavia instead. For end-users,
+this transition should be relatively seamless, because Octavia supports
+the Neutron LBaaS v2 API and it has a similar CLI interface.
+
 
 Sample pillars
 ==============
 
+Octavia API service pillar:
+
 .. code-block:: yaml
 
     octavia:
-      server:
+      api:
         enabled: true
+        version: ocata
+        bind:
+          address: 127.0.0.1
+          port: 9876
+        database:
+          engine: mysql
+          host: 127.0.0.1
+          port: 3306
+          name: octavia
+          user: octavia
+          password: password
+        identity:
+          engine: keystone
+          region: RegionOne
+          host: 127.0.0.1
+          port: 35357
+          user: octavia
+          password: password
+          tenant: service
+        message_queue:
+          engine: rabbitmq
+          host: 127.0.0.1
+          port: 5672
+          user: openstack
+          password: password
+          virtual_host: '/openstack'
+        haproxy_amphora:
+          client_cert: '/etc/octavia/certs/client.pem'
+          server_ca: '/etc/octavia/certs/ca_01.pem'
+
+
+Octavia manager service pillar:
+
+.. code-block:: yaml
+
+    octavia:
+      manager:
+        enabled: true
+        version: ocata
+        database:
+          engine: mysql
+          host: 127.0.0.1
+          port: 3306
+          name: octavia
+          user: octavia
+          password: password
+        identity:
+          engine: keystone
+          region: RegionOne
+          host: 127.0.0.1
+          port: 35357
+          user: octavia
+          password: password
+          tenant: service
+        message_queue:
+          engine: rabbitmq
+          host: 127.0.0.1
+          port: 5672
+          user: openstack
+          password: password
+          virtual_host: '/openstack'
+        certificates:
+          ca_private_key_passphrase: foobar
+          ca_private_key: '/etc/octavia/certs/private/cakey.pem'
+          ca_certificate: '/etc/octavia/certs/ca_01.pem'
+        controller_worker:
+          amp_boot_network_list: '01d3edaa-422c-40b9-b265-425c981691e7'
+          amp_flavor_id: '967972bb-ab54-4679-9f53-bf81d5e28154'
+          amp_image_owner_id: '68520e9f926441ddb37b7c744c4005b7'
+          amp_image_tag: amphora
+          amp_secgroup_list: '9fcd532e-5715-423a-8e3f-51abddbe7705'
+          amp_ssh_key_name: octavia_ssh_key
+          loadbalancer_topology: 'SINGLE'
+        haproxy_amphora:
+          client_cert: '/etc/octavia/certs/client.pem'
+          server_ca: '/etc/octavia/certs/ca_01.pem'
+        health_manager:
+          bind_ip: 192.168.0.12
+          heartbeat_key: 'insecure'
+        house_keeping:
+          spare_amphora_pool_size: 0
+
 
 
 More information
diff --git a/metadata/service/api/cluster.yml b/metadata/service/api/cluster.yml
new file mode 100644
index 0000000..a7d55fd
--- /dev/null
+++ b/metadata/service/api/cluster.yml
@@ -0,0 +1,43 @@
+applications:
+- octavia
+classes:
+- service.octavia.support
+parameters:
+  _param:
+    keystone_octavia_endpoint_type: internal
+    octavia_api_bind_address: ${_param:cluster_local_address}
+    octavia_api_bind_port: 9876
+  octavia:
+    api:
+      enabled: true
+      version: ${_param:octavia_version}
+      debug: true
+      bind:
+        address: ${_param:octavia_api_bind_address}
+        port: ${_param:octavia_api_bind_port}
+      database:
+        engine: mysql
+        host: ${_param:cluster_vip_address}
+        port: 3306
+        name: octavia
+        user: octavia
+        password: ${_param:mysql_octavia_password}
+      identity:
+        engine: keystone
+        region: RegionOne
+        host: ${_param:cluster_vip_address}
+        port: 35357
+        user: octavia
+        password: ${_param:keystone_octavia_password}
+        tenant: service
+        endpoint_type: ${_param:keystone_octavia_endpoint_type}
+      message_queue:
+        engine: rabbitmq
+        host: ${_param:cluster_vip_address}
+        port: 5672
+        user: openstack
+        password: ${_param:rabbitmq_openstack_password}
+        virtual_host: '/openstack'
+      haproxy_amphora:
+        client_cert: '/etc/octavia/certs/client.pem'
+        server_ca: '/etc/octavia/certs/ca_01.pem'
diff --git a/metadata/service/api/single.yml b/metadata/service/api/single.yml
new file mode 100644
index 0000000..532ec9b
--- /dev/null
+++ b/metadata/service/api/single.yml
@@ -0,0 +1,41 @@
+applications:
+- octavia
+classes:
+- service.octavia.support
+parameters:
+  _param:
+    keystone_octavia_endpoint_type: internal
+  octavia:
+    api:
+      enabled: true
+      version: ${_param:octavia_version}
+      debug: true
+      bind:
+        address: ${_param:single_address}
+        port: 9876
+      database:
+        engine: mysql
+        host: ${_param:single_address}
+        port: 3306
+        name: octavia
+        user: octavia
+        password: ${_param:mysql_octavia_password}
+      identity:
+        engine: keystone
+        region: RegionOne
+        host: ${_param:single_address}
+        port: 35357
+        user: octavia
+        password: ${_param:keystone_octavia_password}
+        tenant: service
+        endpoint_type: ${_param:keystone_octavia_endpoint_type}
+      message_queue:
+        engine: rabbitmq
+        host: ${_param:single_address}
+        port: 5672
+        user: openstack
+        password: ${_param:rabbitmq_openstack_password}
+        virtual_host: '/openstack'
+      haproxy_amphora:
+        client_cert: '/etc/octavia/certs/client.pem'
+        server_ca: '/etc/octavia/certs/ca_01.pem'
diff --git a/metadata/service/control/single.yml b/metadata/service/control/single.yml
deleted file mode 100644
index 1d5b3af..0000000
--- a/metadata/service/control/single.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-applications:
-- octavia
-classes:
-- service.octavia.support
-parameters:
-  octavia:
-    server:
-      enabled: true
\ No newline at end of file
diff --git a/metadata/service/manager/single.yml b/metadata/service/manager/single.yml
new file mode 100644
index 0000000..fd05a15
--- /dev/null
+++ b/metadata/service/manager/single.yml
@@ -0,0 +1,55 @@
+applications:
+- octavia
+classes:
+- service.octavia.support
+parameters:
+  _param:
+    keystone_octavia_endpoint_type: internal
+  octavia:
+    manager:
+      enabled: true
+      version: ${_param:octavia_version}
+      debug: true
+      database:
+        engine: mysql
+        host: ${_param:cluster_vip_address}
+        port: 3306
+        name: octavia
+        user: octavia
+        password: ${_param:mysql_octavia_password}
+      identity:
+        engine: keystone
+        region: RegionOne
+        host: ${_param:cluster_vip_address}
+        port: 35357
+        user: octavia
+        password: ${_param:keystone_octavia_password}
+        tenant: service
+        endpoint_type: ${_param:keystone_octavia_endpoint_type}
+      message_queue:
+        engine: rabbitmq
+        host: ${_param:cluster_vip_address}
+        port: 5672
+        user: openstack
+        password: ${_param:rabbitmq_openstack_password}
+        virtual_host: '/openstack'
+      certificates:
+        ca_private_key_passphrase: foobar
+        ca_private_key: '/etc/octavia/certs/private/cakey.pem'
+        ca_certificate: '/etc/octavia/certs/ca_01.pem'
+      controller_worker:
+        amp_boot_network_list: ${_param:amp_boot_network_list}
+        amp_flavor_id: ${_param:amp_flavor_id}
+        amp_image_owner_id: ${_param:amp_image_owner_id}
+        amp_image_tag: amphora
+        amp_secgroup_list: ${_param:amp_secgroup_list}
+        amp_ssh_key_name: octavia_ssh_key
+        loadbalancer_topology: 'SINGLE'
+      haproxy_amphora:
+        client_cert: '/etc/octavia/certs/client.pem'
+        server_ca: '/etc/octavia/certs/ca_01.pem'
+      health_manager:
+        bind_ip: ${_param:octavia_hm_bind_ip}
+        heartbeat_key: 'insecure'
+      house_keeping:
+        spare_amphora_pool_size: 0
diff --git a/octavia/api.sls b/octavia/api.sls
new file mode 100644
index 0000000..fe099f1
--- /dev/null
+++ b/octavia/api.sls
@@ -0,0 +1,49 @@
+{%- from "octavia/map.jinja" import api with context %}
+
+{%- if api.enabled %}
+
+octavia_api_packages:
+  pkg.installed:
+  - names: {{ api.pkgs }}
+
+{%- if pillar.octavia.manager is not defined %}
+/etc/octavia/octavia.conf:
+  file.managed:
+  - source: salt://octavia/files/{{ api.version }}/octavia_api.conf
+  - template: jinja
+  - require:
+    - pkg: octavia_api_packages
+
+/etc/octavia/certificates/openssl.cnf:
+  file.managed:
+  - source: salt://octavia/files/{{ api.version }}/certificates/openssl.cnf
+  - require:
+    - pkg: octavia_api_packages
+
+/etc/octavia/dhcp/dhclient.conf:
+  file.managed:
+  - source: salt://octavia/files/{{ api.version }}/dhcp/dhclient.conf
+  - require:
+    - pkg: octavia_api_packages
+{%- endif %}
+
+{%- if not grains.get('noservices', False) %}
+octavia_db_manage:
+  cmd.run:
+  - name: octavia-db-manage upgrade head
+  - require:
+    - file: /etc/octavia/octavia.conf
+{%- endif %}
+
+{%- if not grains.get('noservices', False) %}
+octavia_api_services:
+  service.running:
+  - names: {{ api.services }}
+  - enable: true
+  - watch:
+    - file: /etc/octavia/octavia.conf
+  - require:
+    - cmd: octavia_db_manage
+{%- endif %}
+
+{%- endif %}
diff --git a/octavia/files/ocata/octavia.conf b/octavia/files/ocata/octavia_api.conf
similarity index 96%
rename from octavia/files/ocata/octavia.conf
rename to octavia/files/ocata/octavia_api.conf
index d4e9b5f..d82d4f7 100644
--- a/octavia/files/ocata/octavia.conf
+++ b/octavia/files/ocata/octavia_api.conf
@@ -1,4 +1,4 @@
-{%- from "octavia/map.jinja" import server with context %}
+{%- from "octavia/map.jinja" import api with context %}
 [DEFAULT]
 
 #
@@ -7,13 +7,13 @@
 
 # The host IP to bind to (IP address value)
 #bind_host = 127.0.0.1
-bind_host = {{ server.bind.address }}
+bind_host = {{ api.bind.address }}
 
 # The port to bind to (port value)
 # Minimum value: 0
 # Maximum value: 65535
 #bind_port = 9876
-bind_port = {{ server.bind.port }}
+bind_port = {{ api.bind.port }}
 
 # The auth strategy for API requests. (string value)
 # Allowed values: noauth, keystone
@@ -55,7 +55,7 @@
 # INFO level. (boolean value)
 # Note: This option can be changed without restarting.
 #debug = false
-debug = {{ server.get('debug', 'False') }}
+debug = {{ api.get('debug', 'False') }}
 
 # DEPRECATED: If set to false, the logging level will be set to WARNING instead
 # of the default INFO level. (boolean value)
@@ -336,14 +336,14 @@
 # A URL representing the messaging driver to use and its full configuration.
 # (string value)
 #transport_url = <None>
-{%- if server.message_queue.members is defined %}
-transport_url = rabbit://{% for member in server.message_queue.members -%}
-                             {{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+{%- if api.message_queue.members is defined %}
+transport_url = rabbit://{% for member in api.message_queue.members -%}
+                             {{ api.message_queue.user }}:{{ api.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
-                             /{{ server.message_queue.virtual_host }}
+                             /{{ api.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ server.message_queue.host }}:{{ server.message_queue.port }}/{{ server.message_queue.virtual_host }}
+transport_url = rabbit://{{ api.message_queue.user }}:{{ api.message_queue.password }}@{{ api.message_queue.host }}:{{ api.message_queue.port }}/{{ api.message_queue.virtual_host }}
 {%- endif %}
 
 # DEPRECATED: The messaging driver to use, defaults to rabbit. Other drivers
@@ -418,17 +418,14 @@
 # Absolute path to the CA Certificate for signing. Defaults
 # to env[OS_OCTAVIA_TLS_CA_CERT].
 # ca_certificate = /etc/ssl/certs/ssl-cert-snakeoil.pem
-ca_certificate =
 
 # Absolute path to the Private Key for signing. Defaults
 # to env[OS_OCTAVIA_TLS_CA_KEY].
 # ca_private_key = /etc/ssl/private/ssl-cert-snakeoil.key
-ca_private_key =
 
 # Passphrase for the Private Key. Defaults
 # to env[OS_OCTAVIA_CA_KEY_PASS] or None.
 # ca_private_key_passphrase =
-ca_private_key_passphrase =
 
 # Certificate signing digest. Defaults
 # to env[OS_OCTAVIA_CA_SIGNING_DIGEST] or "sha256".
@@ -460,6 +457,7 @@
 
 # The endpoint_type to be used for barbican service. (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ api.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -484,13 +482,11 @@
 
 # Nova instance flavor id for the Amphora (string value)
 #amp_flavor_id =
-amp_flavor_id = 92b3fb9c-2ef2-4964-b145-3bbc02646353
 
 # Glance image tag for the Amphora image to boot. Use this option to be able to
 # update the image without reconfiguring Octavia. Ignored if amp_image_id is
 # defined. (string value)
 #amp_image_tag =
-amp_image_tag = amphora
 
 # DEPRECATED: Glance image id for the Amphora image to boot (string value)
 # This option is deprecated for removal.
@@ -501,11 +497,9 @@
 # Restrict glance image selection to a specific owner ID.  This is a
 # recommended security setting. (string value)
 #amp_image_owner_id =
-amp_image_owner_id = 625e4ec921b8496ead595c25e289578e
 
 # SSH key name used to boot the Amphora (string value)
 #amp_ssh_key_name =
-amp_ssh_key_name = octavia_ssh_key
 
 # Determines whether or not to allow access to the Amphorae (boolean value)
 #amp_ssh_access_allowed = true
@@ -513,7 +507,6 @@
 # List of networks to attach to the Amphorae. All networks defined in the list
 # will be attached to each amphora. (list value)
 #amp_boot_network_list =
-amp_boot_network_list = 78273e19-2acc-4f88-8af2-47430084db16
 
 # DEPRECATED: Network to attach to the Amphorae. (string value)
 # This option is deprecated for removal.
@@ -523,7 +516,6 @@
 
 # List of security groups to attach to the Amphora. (list value)
 #amp_secgroup_list =
-amp_secgroup_list = 996fbc7f-b3c3-4959-8f04-2273681dc854
 
 # Client CA for the amphora agent to use (string value)
 #client_ca = /etc/octavia/certs/ca_01.pem
@@ -544,7 +536,6 @@
 # ACTIVE_STANDBY - Two amphora per load balancer. (string value)
 # Allowed values: ACTIVE_STANDBY, SINGLE
 #loadbalancer_topology = SINGLE
-loadbalancer_topology =
 
 # If True, build cloud-init user-data that is passed to the config drive on
 # Amphora boot instead of personality files. If False, utilize personality
@@ -561,40 +552,40 @@
 # requests "origin" header. Format: "<protocol>://<host>[:<port>]", no trailing
 # slash. Example: https://horizon.example.com (list value)
 #allowed_origin = <None>
-{%- if server.cors.allowed_origin is defined %}
-allowed_origin = {{ server.cors.allowed_origin }}
+{%- if api.cors.allowed_origin is defined %}
+allowed_origin = {{ api.cors.allowed_origin }}
 {%- endif %}
 
 # Indicate that the actual request can include user credentials (boolean value)
 #allow_credentials = true
-{%- if server.cors.allow_credentials is defined %}
-allow_credentials = {{ server.cors.allow_credentials }}
+{%- if api.cors.allow_credentials is defined %}
+allow_credentials = {{ api.cors.allow_credentials }}
 {%- endif %}
 
 # Indicate which headers are safe to expose to the API. Defaults to HTTP Simple
 # Headers. (list value)
 #expose_headers =
-{%- if server.cors.expose_headers is defined %}
-expose_headers = {{ server.cors.expose_headers }}
+{%- if api.cors.expose_headers is defined %}
+expose_headers = {{ api.cors.expose_headers }}
 {%- endif %}
 
 # Maximum cache age of CORS preflight requests. (integer value)
 #max_age = 3600
-{%- if server.cors.max_age is defined %}
-max_age = {{ server.cors.max_age }}
+{%- if api.cors.max_age is defined %}
+max_age = {{ api.cors.max_age }}
 {%- endif %}
 
 # Indicate which methods can be used during the actual request. (list value)
 #allow_methods = OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,PATCH
-{%- if server.cors.allow_methods is defined %}
-allow_methods = {{ server.cors.allow_methods }}
+{%- if api.cors.allow_methods is defined %}
+allow_methods = {{ api.cors.allow_methods }}
 {%- endif %}
 
 # Indicate which header field names may be used during the actual request.
 # (list value)
 #allow_headers =
-{%- if server.cors.allow_headers is defined %}
-allow_headers = {{ server.cors.allow_headers }}
+{%- if api.cors.allow_headers is defined %}
+allow_headers = {{ api.cors.allow_headers }}
 {%- endif %}
 
 [cors.subdomain]
@@ -653,7 +644,7 @@
 # Deprecated group/name - [DATABASE]/sql_connection
 # Deprecated group/name - [sql]/connection
 #connection = <None>
-connection = {{ server.database.engine }}+pymysql://{{ server.database.user }}:{{ server.database.password }}@{{ server.database.host }}/{{ server.database.name }}?charset=utf8
+connection = {{ api.database.engine }}+pymysql://{{ api.database.user }}:{{ api.database.password }}@{{ api.database.host }}/{{ api.database.name }}?charset=utf8
 
 # The SQLAlchemy connection string to use to connect to the slave database.
 # (string value)
@@ -751,6 +742,7 @@
 
 # Endpoint interface in identity service to use (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ api.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -821,11 +813,11 @@
 
 # The client certificate to talk to the agent (string value)
 #client_cert = /etc/octavia/certs/client.pem
-client_cert = /etc/octavia/certs/client.pem
+client_cert = {{ api.haproxy_amphora.client_cert }}
 
 # The ca which signed the server certificates (string value)
 #server_ca = /etc/octavia/certs/server_ca.pem
-server_ca = /etc/octavia/certs/ca_01.pem
+server_ca = {{ api.haproxy_amphora.server_ca }}
 
 # DEPRECATED: If False, use sysvinit. (boolean value)
 # This option is deprecated for removal.
@@ -841,9 +833,8 @@
 
 # IP address the controller will listen on for heart beats (IP address value)
 #bind_ip = 127.0.0.1
-bind_ip =
 
-# Port number the controller will listen onfor heart beats (port value)
+# Port number the controller will listen on for heart beats (port value)
 # Minimum value: 0
 # Maximum value: 65535
 #bind_port = 5555
@@ -857,7 +848,6 @@
 
 # key used to validate amphora sending the message (string value)
 #heartbeat_key = <None>
-heartbeat_key = insecure
 
 # Interval, in seconds, to wait before failing over an amphora. (integer value)
 #heartbeat_timeout = 60
@@ -871,7 +861,6 @@
 # List of controller ip and port pairs for the heartbeat receivers. Example
 # 127.0.0.1:5555, 192.168.0.1:5555 (list value)
 #controller_ip_port_list =
-controller_ip_port_list =
 
 # Sleep time between sending heartbeats. (integer value)
 #heartbeat_interval = 10
@@ -956,7 +945,7 @@
 # because normal end users may not be  able to reach that endpoint. (string
 # value)
 #auth_uri = <None>
-auth_uri=http://{{ server.identity.host }}:5000
+auth_uri=http://{{ api.identity.host }}:5000
 
 # API version of the admin Identity API endpoint. (string value)
 #auth_version = <None>
@@ -1009,8 +998,8 @@
 # undefined, tokens will instead be cached in-process. (list value)
 # Deprecated group/name - [keystone_authtoken]/memcache_servers
 #memcached_servers = <None>
-{%- if server.cache is defined %}
-memcached_servers={%- for member in server.cache.members %}{{ member.host }}:11211{% if not loop.last %},{% endif %}{%- endfor %}
+{%- if api.cache is defined %}
+memcached_servers={%- for member in api.cache.members %}{{ member.host }}:11211{% if not loop.last %},{% endif %}{%- endfor %}
 {%- endif %}
 
 # In order to prevent excessive effort spent validating tokens, the middleware
@@ -1205,6 +1194,7 @@
 
 # Endpoint interface in identity service to use (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ api.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -1231,6 +1221,7 @@
 
 # Endpoint interface in identity service to use (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ api.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -1500,6 +1491,14 @@
 # messaging, messagingv2, routing, log, test, noop (multi valued)
 # Deprecated group/name - [DEFAULT]/notification_driver
 #driver =
+{%- if api.notification is mapping %}
+driver = {{ api.notification.get('driver', 'messagingv2') }}
+{%- if api.notification.topics is defined %}
+topics = {{ api.notification.topics }}
+{%- endif %}
+{%- elif api.notification %}
+driver = messagingv2
+{%- endif %}
 
 # A URL representing the messaging driver to use for notifications. If not set,
 # we fall back to the same configuration used for RPC. (string value)
@@ -1969,7 +1968,7 @@
 
 # Authentication URL (string value)
 #auth_url = <None>
-auth_url=http://{{ server.identity.host }}:35357
+auth_url=http://{{ api.identity.host }}:35357
 
 # Authentication type to load (string value)
 # Deprecated group/name - [service_auth]/auth_plugin
@@ -1979,7 +1978,6 @@
 # PEM encoded Certificate Authority to use when verifying HTTPs connections.
 # (string value)
 #cafile = <None>
-cafile =
 
 # PEM encoded client certificate cert file (string value)
 #certfile = <None>
@@ -2008,11 +2006,11 @@
 
 # User's password (string value)
 #password = <None>
-password = {{ server.identity.password }}
+password = {{ api.identity.password }}
 
 # Domain ID containing project (string value)
 #project_domain_id = <None>
-project_domain_id = {{ server.identity.get('domain', 'default') }}
+project_domain_id = {{ api.identity.get('domain', 'default') }}
 
 # Domain name containing project (string value)
 #project_domain_name = <None>
@@ -2024,7 +2022,7 @@
 # Project name to scope to (string value)
 # Deprecated group/name - [service_auth]/tenant-name
 #project_name = <None>
-project_name = {{ server.identity.tenant }}
+project_name = {{ api.identity.tenant }}
 
 # Tenant ID (string value)
 #tenant_id = <None>
@@ -2040,7 +2038,7 @@
 
 # User's domain id (string value)
 #user_domain_id = <None>
-user_domain_id = {{ server.identity.get('domain', 'default') }}
+user_domain_id = {{ api.identity.get('domain', 'default') }}
 
 # User's domain name (string value)
 #user_domain_name = <None>
@@ -2051,7 +2049,7 @@
 # Username (string value)
 # Deprecated group/name - [service_auth]/user-name
 #username = <None>
-username = {{ server.identity.user }}
+username = {{ api.identity.user }}
 
 
 [task_flow]
diff --git a/octavia/files/ocata/octavia.conf b/octavia/files/ocata/octavia_manager.conf
similarity index 94%
copy from octavia/files/ocata/octavia.conf
copy to octavia/files/ocata/octavia_manager.conf
index d4e9b5f..a140054 100644
--- a/octavia/files/ocata/octavia.conf
+++ b/octavia/files/ocata/octavia_manager.conf
@@ -1,4 +1,5 @@
-{%- from "octavia/map.jinja" import server with context %}
+{%- from "octavia/map.jinja" import api with context %}
+{%- from "octavia/map.jinja" import manager with context %}
 [DEFAULT]
 
 #
@@ -7,13 +8,17 @@
 
 # The host IP to bind to (IP address value)
 #bind_host = 127.0.0.1
-bind_host = {{ server.bind.address }}
+{%- if pillar.octavia.api is defined %}
+bind_host = {{ api.bind.address }}
+{% endif %}
 
 # The port to bind to (port value)
 # Minimum value: 0
 # Maximum value: 65535
 #bind_port = 9876
-bind_port = {{ server.bind.port }}
+{%- if pillar.octavia.api is defined %}
+bind_port = {{ api.bind.port }}
+{% endif %}
 
 # The auth strategy for API requests. (string value)
 # Allowed values: noauth, keystone
@@ -55,7 +60,7 @@
 # INFO level. (boolean value)
 # Note: This option can be changed without restarting.
 #debug = false
-debug = {{ server.get('debug', 'False') }}
+debug = {{ manager.get('debug', 'False') }}
 
 # DEPRECATED: If set to false, the logging level will be set to WARNING instead
 # of the default INFO level. (boolean value)
@@ -336,14 +341,14 @@
 # A URL representing the messaging driver to use and its full configuration.
 # (string value)
 #transport_url = <None>
-{%- if server.message_queue.members is defined %}
-transport_url = rabbit://{% for member in server.message_queue.members -%}
-                             {{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
+{%- if manager.message_queue.members is defined %}
+transport_url = rabbit://{% for member in manager.message_queue.members -%}
+                             {{ manager.message_queue.user }}:{{ manager.message_queue.password }}@{{ member.host }}:{{ member.get('port', 5672) }}
                              {%- if not loop.last -%},{%- endif -%}
                          {%- endfor -%}
-                             /{{ server.message_queue.virtual_host }}
+                             /{{ manager.message_queue.virtual_host }}
 {%- else %}
-transport_url = rabbit://{{ server.message_queue.user }}:{{ server.message_queue.password }}@{{ server.message_queue.host }}:{{ server.message_queue.port }}/{{ server.message_queue.virtual_host }}
+transport_url = rabbit://{{ manager.message_queue.user }}:{{ manager.message_queue.password }}@{{ manager.message_queue.host }}:{{ manager.message_queue.port }}/{{ manager.message_queue.virtual_host }}
 {%- endif %}
 
 # DEPRECATED: The messaging driver to use, defaults to rabbit. Other drivers
@@ -418,17 +423,17 @@
 # Absolute path to the CA Certificate for signing. Defaults
 # to env[OS_OCTAVIA_TLS_CA_CERT].
 # ca_certificate = /etc/ssl/certs/ssl-cert-snakeoil.pem
-ca_certificate =
+ca_certificate = {{ manager.certificates.ca_certificate }}
 
 # Absolute path to the Private Key for signing. Defaults
 # to env[OS_OCTAVIA_TLS_CA_KEY].
 # ca_private_key = /etc/ssl/private/ssl-cert-snakeoil.key
-ca_private_key =
+ca_private_key = {{ manager.certificates.ca_private_key }}
 
 # Passphrase for the Private Key. Defaults
 # to env[OS_OCTAVIA_CA_KEY_PASS] or None.
 # ca_private_key_passphrase =
-ca_private_key_passphrase =
+ca_private_key_passphrase = {{ manager.certificates.ca_private_key_passphrase }}
 
 # Certificate signing digest. Defaults
 # to env[OS_OCTAVIA_CA_SIGNING_DIGEST] or "sha256".
@@ -460,6 +465,7 @@
 
 # The endpoint_type to be used for barbican service. (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ manager.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -484,13 +490,13 @@
 
 # Nova instance flavor id for the Amphora (string value)
 #amp_flavor_id =
-amp_flavor_id = 92b3fb9c-2ef2-4964-b145-3bbc02646353
+amp_flavor_id = {{ manager.controller_worker.amp_flavor_id }}
 
 # Glance image tag for the Amphora image to boot. Use this option to be able to
 # update the image without reconfiguring Octavia. Ignored if amp_image_id is
 # defined. (string value)
 #amp_image_tag =
-amp_image_tag = amphora
+amp_image_tag = {{ manager.controller_worker.amp_image_tag }}
 
 # DEPRECATED: Glance image id for the Amphora image to boot (string value)
 # This option is deprecated for removal.
@@ -501,11 +507,11 @@
 # Restrict glance image selection to a specific owner ID.  This is a
 # recommended security setting. (string value)
 #amp_image_owner_id =
-amp_image_owner_id = 625e4ec921b8496ead595c25e289578e
+amp_image_owner_id = {{ manager.controller_worker.amp_image_owner_id }}
 
 # SSH key name used to boot the Amphora (string value)
 #amp_ssh_key_name =
-amp_ssh_key_name = octavia_ssh_key
+amp_ssh_key_name = {{ manager.controller_worker.amp_ssh_key_name }}
 
 # Determines whether or not to allow access to the Amphorae (boolean value)
 #amp_ssh_access_allowed = true
@@ -513,7 +519,7 @@
 # List of networks to attach to the Amphorae. All networks defined in the list
 # will be attached to each amphora. (list value)
 #amp_boot_network_list =
-amp_boot_network_list = 78273e19-2acc-4f88-8af2-47430084db16
+amp_boot_network_list = {{ manager.controller_worker.amp_boot_network_list }}
 
 # DEPRECATED: Network to attach to the Amphorae. (string value)
 # This option is deprecated for removal.
@@ -523,7 +529,7 @@
 
 # List of security groups to attach to the Amphora. (list value)
 #amp_secgroup_list =
-amp_secgroup_list = 996fbc7f-b3c3-4959-8f04-2273681dc854
+amp_secgroup_list = {{ manager.controller_worker.amp_secgroup_list }}
 
 # Client CA for the amphora agent to use (string value)
 #client_ca = /etc/octavia/certs/ca_01.pem
@@ -544,7 +550,7 @@
 # ACTIVE_STANDBY - Two amphora per load balancer. (string value)
 # Allowed values: ACTIVE_STANDBY, SINGLE
 #loadbalancer_topology = SINGLE
-loadbalancer_topology =
+loadbalancer_topology = {{ manager.controller_worker.loadbalancer_topology }}
 
 # If True, build cloud-init user-data that is passed to the config drive on
 # Amphora boot instead of personality files. If False, utilize personality
@@ -561,41 +567,53 @@
 # requests "origin" header. Format: "<protocol>://<host>[:<port>]", no trailing
 # slash. Example: https://horizon.example.com (list value)
 #allowed_origin = <None>
-{%- if server.cors.allowed_origin is defined %}
-allowed_origin = {{ server.cors.allowed_origin }}
+{%- if pillar.octavia.api is defined %}
+{%- if api.cors.allowed_origin is defined %}
+allowed_origin = {{ api.cors.allowed_origin }}
 {%- endif %}
+{% endif %}
 
 # Indicate that the actual request can include user credentials (boolean value)
 #allow_credentials = true
-{%- if server.cors.allow_credentials is defined %}
-allow_credentials = {{ server.cors.allow_credentials }}
+{%- if pillar.octavia.api is defined %}
+{%- if api.cors.allow_credentials is defined %}
+allow_credentials = {{ api.cors.allow_credentials }}
 {%- endif %}
+{% endif %}
 
 # Indicate which headers are safe to expose to the API. Defaults to HTTP Simple
 # Headers. (list value)
 #expose_headers =
-{%- if server.cors.expose_headers is defined %}
-expose_headers = {{ server.cors.expose_headers }}
+{%- if pillar.octavia.api is defined %}
+{%- if api.cors.expose_headers is defined %}
+expose_headers = {{ api.cors.expose_headers }}
 {%- endif %}
+{% endif %}
 
 # Maximum cache age of CORS preflight requests. (integer value)
 #max_age = 3600
-{%- if server.cors.max_age is defined %}
-max_age = {{ server.cors.max_age }}
+{%- if pillar.octavia.api is defined %}
+{%- if api.cors.max_age is defined %}
+max_age = {{ api.cors.max_age }}
 {%- endif %}
+{% endif %}
 
 # Indicate which methods can be used during the actual request. (list value)
 #allow_methods = OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,PATCH
-{%- if server.cors.allow_methods is defined %}
-allow_methods = {{ server.cors.allow_methods }}
+{%- if pillar.octavia.api is defined %}
+{%- if api.cors.allow_methods is defined %}
+allow_methods = {{ api.cors.allow_methods }}
 {%- endif %}
+{% endif %}
 
 # Indicate which header field names may be used during the actual request.
 # (list value)
 #allow_headers =
-{%- if server.cors.allow_headers is defined %}
-allow_headers = {{ server.cors.allow_headers }}
+{%- if pillar.octavia.api is defined %}
+{%- if api.cors.allow_headers is defined %}
+allow_headers = {{ api.cors.allow_headers }}
 {%- endif %}
+{% endif %}
 
 [cors.subdomain]
 
@@ -653,7 +671,7 @@
 # Deprecated group/name - [DATABASE]/sql_connection
 # Deprecated group/name - [sql]/connection
 #connection = <None>
-connection = {{ server.database.engine }}+pymysql://{{ server.database.user }}:{{ server.database.password }}@{{ server.database.host }}/{{ server.database.name }}?charset=utf8
+connection = {{ manager.database.engine }}+pymysql://{{ manager.database.user }}:{{ manager.database.password }}@{{ manager.database.host }}/{{ manager.database.name }}?charset=utf8
 
 # The SQLAlchemy connection string to use to connect to the slave database.
 # (string value)
@@ -751,6 +769,7 @@
 
 # Endpoint interface in identity service to use (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ manager.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -821,11 +840,11 @@
 
 # The client certificate to talk to the agent (string value)
 #client_cert = /etc/octavia/certs/client.pem
-client_cert = /etc/octavia/certs/client.pem
+client_cert = {{ manager.haproxy_amphora.client_cert }}
 
 # The ca which signed the server certificates (string value)
 #server_ca = /etc/octavia/certs/server_ca.pem
-server_ca = /etc/octavia/certs/ca_01.pem
+server_ca = {{ manager.haproxy_amphora.server_ca }}
 
 # DEPRECATED: If False, use sysvinit. (boolean value)
 # This option is deprecated for removal.
@@ -841,9 +860,9 @@
 
 # IP address the controller will listen on for heart beats (IP address value)
 #bind_ip = 127.0.0.1
-bind_ip =
+bind_ip = {{ manager.health_manager.bind_ip }}
 
-# Port number the controller will listen onfor heart beats (port value)
+# Port number the controller will listen on for heart beats (port value)
 # Minimum value: 0
 # Maximum value: 65535
 #bind_port = 5555
@@ -857,7 +876,7 @@
 
 # key used to validate amphora sending the message (string value)
 #heartbeat_key = <None>
-heartbeat_key = insecure
+heartbeat_key = {{ manager.health_manager.heartbeat_key }}
 
 # Interval, in seconds, to wait before failing over an amphora. (integer value)
 #heartbeat_timeout = 60
@@ -871,7 +890,7 @@
 # List of controller ip and port pairs for the heartbeat receivers. Example
 # 127.0.0.1:5555, 192.168.0.1:5555 (list value)
 #controller_ip_port_list =
-controller_ip_port_list =
+controller_ip_port_list = {{ manager.health_manager.bind_ip }}:5555
 
 # Sleep time between sending heartbeats. (integer value)
 #heartbeat_interval = 10
@@ -892,6 +911,7 @@
 
 # Number of spare amphorae (integer value)
 #spare_amphora_pool_size = 0
+spare_amphora_pool_size = {{ manager.house_keeping.spare_amphora_pool_size }}
 
 # DB cleanup interval in seconds (integer value)
 #cleanup_interval = 30
@@ -956,7 +976,7 @@
 # because normal end users may not be  able to reach that endpoint. (string
 # value)
 #auth_uri = <None>
-auth_uri=http://{{ server.identity.host }}:5000
+auth_uri=http://{{ manager.identity.host }}:5000
 
 # API version of the admin Identity API endpoint. (string value)
 #auth_version = <None>
@@ -1009,8 +1029,8 @@
 # undefined, tokens will instead be cached in-process. (list value)
 # Deprecated group/name - [keystone_authtoken]/memcache_servers
 #memcached_servers = <None>
-{%- if server.cache is defined %}
-memcached_servers={%- for member in server.cache.members %}{{ member.host }}:11211{% if not loop.last %},{% endif %}{%- endfor %}
+{%- if manager.cache is defined %}
+memcached_servers={%- for member in manager.cache.members %}{{ member.host }}:11211{% if not loop.last %},{% endif %}{%- endfor %}
 {%- endif %}
 
 # In order to prevent excessive effort spent validating tokens, the middleware
@@ -1205,6 +1225,7 @@
 
 # Endpoint interface in identity service to use (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ manager.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -1231,6 +1252,7 @@
 
 # Endpoint interface in identity service to use (string value)
 #endpoint_type = publicURL
+endpoint_type = {{ manager.identity.get('endpoint_type', 'public') }}
 
 # CA certificates file path (string value)
 #ca_certificates_file = <None>
@@ -1500,6 +1522,14 @@
 # messaging, messagingv2, routing, log, test, noop (multi valued)
 # Deprecated group/name - [DEFAULT]/notification_driver
 #driver =
+{%- if manager.notification is mapping %}
+driver = {{ manager.notification.get('driver', 'messagingv2') }}
+{%- if manager.notification.topics is defined %}
+topics = {{ manager.notification.topics }}
+{%- endif %}
+{%- elif manager.notification %}
+driver = messagingv2
+{%- endif %}
 
 # A URL representing the messaging driver to use for notifications. If not set,
 # we fall back to the same configuration used for RPC. (string value)
@@ -1969,7 +1999,7 @@
 
 # Authentication URL (string value)
 #auth_url = <None>
-auth_url=http://{{ server.identity.host }}:35357
+auth_url=http://{{ manager.identity.host }}:35357
 
 # Authentication type to load (string value)
 # Deprecated group/name - [service_auth]/auth_plugin
@@ -1979,7 +2009,6 @@
 # PEM encoded Certificate Authority to use when verifying HTTPs connections.
 # (string value)
 #cafile = <None>
-cafile =
 
 # PEM encoded client certificate cert file (string value)
 #certfile = <None>
@@ -2008,11 +2037,11 @@
 
 # User's password (string value)
 #password = <None>
-password = {{ server.identity.password }}
+password = {{ manager.identity.password }}
 
 # Domain ID containing project (string value)
 #project_domain_id = <None>
-project_domain_id = {{ server.identity.get('domain', 'default') }}
+project_domain_id = {{ manager.identity.get('domain', 'default') }}
 
 # Domain name containing project (string value)
 #project_domain_name = <None>
@@ -2024,7 +2053,7 @@
 # Project name to scope to (string value)
 # Deprecated group/name - [service_auth]/tenant-name
 #project_name = <None>
-project_name = {{ server.identity.tenant }}
+project_name = {{ manager.identity.tenant }}
 
 # Tenant ID (string value)
 #tenant_id = <None>
@@ -2040,7 +2069,7 @@
 
 # User's domain id (string value)
 #user_domain_id = <None>
-user_domain_id = {{ server.identity.get('domain', 'default') }}
+user_domain_id = {{ manager.identity.get('domain', 'default') }}
 
 # User's domain name (string value)
 #user_domain_name = <None>
@@ -2051,7 +2080,7 @@
 # Username (string value)
 # Deprecated group/name - [service_auth]/user-name
 #username = <None>
-username = {{ server.identity.user }}
+username = {{ manager.identity.user }}
 
 
 [task_flow]
diff --git a/octavia/init.sls b/octavia/init.sls
index 9770a06..60080f9 100644
--- a/octavia/init.sls
+++ b/octavia/init.sls
@@ -1,4 +1,7 @@
 include:
-{%- if pillar.octavia.server is defined %}
-- octavia.server
+{%- if pillar.octavia.api is defined %}
+- octavia.api
+{%- endif %}
+{%- if pillar.octavia.manager is defined %}
+- octavia.manager
 {%- endif %}
diff --git a/octavia/manager.sls b/octavia/manager.sls
new file mode 100644
index 0000000..d0b0e89
--- /dev/null
+++ b/octavia/manager.sls
@@ -0,0 +1,37 @@
+{%- from "octavia/map.jinja" import manager with context %}
+
+{%- if manager.enabled %}
+
+octavia_manager_packages:
+  pkg.installed:
+  - names: {{ manager.pkgs }}
+
+/etc/octavia/octavia.conf:
+  file.managed:
+  - source: salt://octavia/files/{{ manager.version }}/octavia_manager.conf
+  - template: jinja
+  - require:
+    - pkg: octavia_manager_packages
+
+/etc/octavia/certificates/openssl.cnf:
+  file.managed:
+  - source: salt://octavia/files/{{ manager.version }}/certificates/openssl.cnf
+  - require:
+    - pkg: octavia_manager_packages
+
+/etc/octavia/dhcp/dhclient.conf:
+  file.managed:
+  - source: salt://octavia/files/{{ manager.version }}/dhcp/dhclient.conf
+  - require:
+    - pkg: octavia_manager_packages
+
+{%- if not grains.get('noservices', False) %}
+octavia_manager_services:
+  service.running:
+  - names: {{ manager.services }}
+  - enable: true
+  - watch:
+    - file: /etc/octavia/octavia.conf
+{%- endif %}
+
+{%- endif %}
diff --git a/octavia/map.jinja b/octavia/map.jinja
index dd440b0..2052389 100644
--- a/octavia/map.jinja
+++ b/octavia/map.jinja
@@ -1,10 +1,41 @@
+{% set api = salt['grains.filter_by']({
+    'Debian': {
+        'pkgs': ['octavia-common', 'octavia-api'],
+        'services': ['octavia-api'],
+        'notification': False,
+        'cors': {},
+        'audit': {
+          'enabled': false
+        }
+    },
+    'RedHat': {
+        'pkgs': ['octavia-common', 'octavia-api'],
+        'services': ['octavia-api'],
+        'notification': False,
+        'cors': {},
+        'audit': {
+          'enabled': false
+        }
+    },
+}, merge=pillar.octavia.get('api', {})) %}
 
-{%- load_yaml as base_defaults %}
-
-Debian:
-  pkgs:
-  - octavia
-
-{%- endload %}
-
-{%- set server = salt['grains.filter_by'](base_defaults, merge=salt['pillar.get']('octavia:server')) %}
+{% set manager = salt['grains.filter_by']({
+    'Debian': {
+        'pkgs': ['octavia-common', 'octavia-worker', 'octavia-housekeeping', 'octavia-health-manager'],
+        'services': ['octavia-worker', 'octavia-housekeeping', 'octavia-health-manager'],
+        'notification': False,
+        'cors': {},
+        'audit': {
+          'enabled': false
+        }
+    },
+    'RedHat': {
+        'pkgs': ['octavia-common', 'octavia-worker', 'octavia-housekeeping', 'octavia-health-manager'],
+        'services': ['octavia-worker', 'octavia-housekeeping', 'octavia-health-manager'],
+        'notification': False,
+        'cors': {},
+        'audit': {
+          'enabled': false
+        }
+    },
+}, merge=pillar.octavia.get('manager', {})) %}
diff --git a/octavia/server.sls b/octavia/server.sls
deleted file mode 100644
index c188440..0000000
--- a/octavia/server.sls
+++ /dev/null
@@ -1,9 +0,0 @@
-{%- from "octavia/map.jinja" import server with context %}
-
-{%- if server.enabled %}
-
-show_uptime:
-  cmd.run:
-    - name: uptime
-
-{%- endif %}
diff --git a/tests/pillar/api_cluster.sls b/tests/pillar/api_cluster.sls
new file mode 100644
index 0000000..645b0be
--- /dev/null
+++ b/tests/pillar/api_cluster.sls
@@ -0,0 +1,36 @@
+octavia:
+  api:
+    enabled: true
+    version: ocata
+    bind:
+      address: 127.0.0.1
+      port: 9876
+    database:
+      engine: mysql
+      host: 127.0.0.1
+      port: 3306
+      name: octavia
+      user: octavia
+      password: password
+    identity:
+      engine: keystone
+      region: RegionOne
+      host: 127.0.0.1
+      port: 35357
+      user: octavia
+      password: password
+      tenant: service
+    message_queue:
+      engine: rabbitmq
+      host: 127.0.0.1
+      port: 5672
+      user: openstack
+      password: password
+      virtual_host: '/openstack'
+      members:
+      - host: 127.0.0.1
+      - host: 127.0.1.1
+      - host: 127.0.2.1
+    haproxy_amphora:
+        client_cert: '/etc/octavia/certs/client.pem'
+        server_ca: '/etc/octavia/certs/ca_01.pem'
diff --git a/tests/pillar/api_single.sls b/tests/pillar/api_single.sls
new file mode 100644
index 0000000..29ab5f7
--- /dev/null
+++ b/tests/pillar/api_single.sls
@@ -0,0 +1,32 @@
+octavia:
+  api:
+    enabled: true
+    version: ocata
+    bind:
+      address: 127.0.0.1
+      port: 9876
+    database:
+      engine: mysql
+      host: 127.0.0.1
+      port: 3306
+      name: octavia
+      user: octavia
+      password: password
+    identity:
+      engine: keystone
+      region: RegionOne
+      host: 127.0.0.1
+      port: 35357
+      user: octavia
+      password: password
+      tenant: service
+    message_queue:
+      engine: rabbitmq
+      host: 127.0.0.1
+      port: 5672
+      user: openstack
+      password: password
+      virtual_host: '/openstack'
+    haproxy_amphora:
+        client_cert: '/etc/octavia/certs/client.pem'
+        server_ca: '/etc/octavia/certs/ca_01.pem'
diff --git a/tests/pillar/manager_single.sls b/tests/pillar/manager_single.sls
new file mode 100644
index 0000000..f0cba83
--- /dev/null
+++ b/tests/pillar/manager_single.sls
@@ -0,0 +1,46 @@
+octavia:
+  manager:
+    enabled: true
+    version: ocata
+    database:
+      engine: mysql
+      host: 127.0.0.1
+      port: 3306
+      name: octavia
+      user: octavia
+      password: password
+    identity:
+      engine: keystone
+      region: RegionOne
+      host: 127.0.0.1
+      port: 35357
+      user: octavia
+      password: password
+      tenant: service
+    message_queue:
+      engine: rabbitmq
+      host: 127.0.0.1
+      port: 5672
+      user: openstack
+      password: password
+      virtual_host: '/openstack'
+    certificates:
+      ca_private_key_passphrase: foobar
+      ca_private_key: '/etc/octavia/certs/private/cakey.pem'
+      ca_certificate: '/etc/octavia/certs/ca_01.pem'
+    controller_worker:
+      amp_boot_network_list: '01d3edaa-422c-40b9-b265-425c981691e7'
+      amp_flavor_id: '967972bb-ab54-4679-9f53-bf81d5e28154'
+      amp_image_owner_id: '68520e9f926441ddb37b7c744c4005b7'
+      amp_image_tag: amphora
+      amp_secgroup_list: '9fcd532e-5715-423a-8e3f-51abddbe7705'
+      amp_ssh_key_name: octavia_ssh_key
+      loadbalancer_topology: 'SINGLE'
+    haproxy_amphora:
+      client_cert: '/etc/octavia/certs/client.pem'
+      server_ca: '/etc/octavia/certs/ca_01.pem'
+    health_manager:
+      bind_ip: 192.168.0.12
+      heartbeat_key: 'insecure'
+    house_keeping:
+      spare_amphora_pool_size: 0
diff --git a/tests/pillar/repo_openstack.sls b/tests/pillar/repo_openstack.sls
new file mode 100644
index 0000000..a638cf6
--- /dev/null
+++ b/tests/pillar/repo_openstack.sls
@@ -0,0 +1,8 @@
+linux:
+  system:
+    enabled: true
+    repo:
+      mirantis_openstack:
+        source: "deb http://mirror.fuel-infra.org/mcp-repos/ocata/xenial ocata main"
+        architectures: amd64
+        key_url: "http://mirror.fuel-infra.org/mcp-repos/ocata/xenial/archive-mcpocata.key"
diff --git a/tests/pillar/single.sls b/tests/pillar/single.sls
deleted file mode 100644
index 770a636..0000000
--- a/tests/pillar/single.sls
+++ /dev/null
@@ -1,3 +0,0 @@
-octavia:
-  server:
-    enabled: true