Andrew Bogott has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/405373 )
Change subject: openstack horizon: rough in manifests for source deploy of
Horizon 'ocata'
......................................................................
openstack horizon: rough in manifests for source deploy of Horizon 'ocata'
At the moment I'm only trying to get a stock Horizon install; will add our
customizations later (ideally via scap rather than puppet)
Bug: T168470
Change-Id: Ie3cc3df3b48a4933672f98d574551768e4f82314
---
M hieradata/role/common/deployment_server.yaml
A modules/openstack/files/ocata/horizon/disabled_policy.json
A modules/openstack/files/ocata/horizon/glance_policy.json
A modules/openstack/files/ocata/horizon/keystone_policy.json
A modules/openstack/files/ocata/horizon/nova_policy.json
A modules/openstack/manifests/horizon/source_deploy.pp
A modules/openstack/templates/ocata/horizon/labtesthorizon.wikimedia.org.erb
A modules/openstack/templates/ocata/horizon/local_settings.py.erb
A modules/openstack/templates/ocata/horizon/newhorizon.wikimedia.org.erb
9 files changed, 1,069 insertions(+), 0 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/operations/puppet
refs/changes/73/405373/1
diff --git a/hieradata/role/common/deployment_server.yaml
b/hieradata/role/common/deployment_server.yaml
index 1921c65..d8ed935 100644
--- a/hieradata/role/common/deployment_server.yaml
+++ b/hieradata/role/common/deployment_server.yaml
@@ -141,6 +141,8 @@
repository: operations/software/servermon
striker/deploy:
repository: labs/striker/deploy
+ horizon/deploy:
+ repository: openstack/horizon/deploy
tilerator/deploy:
repository: maps/tilerator/deploy
# lvs_service: tilerator
diff --git a/modules/openstack/files/ocata/horizon/disabled_policy.json
b/modules/openstack/files/ocata/horizon/disabled_policy.json
new file mode 100644
index 0000000..92503af
--- /dev/null
+++ b/modules/openstack/files/ocata/horizon/disabled_policy.json
@@ -0,0 +1,4 @@
+{
+ "context_is_admin": "!",
+ "default": "!"
+}
diff --git a/modules/openstack/files/ocata/horizon/glance_policy.json
b/modules/openstack/files/ocata/horizon/glance_policy.json
new file mode 100644
index 0000000..ce70040
--- /dev/null
+++ b/modules/openstack/files/ocata/horizon/glance_policy.json
@@ -0,0 +1,52 @@
+{
+ "context_is_admin": "role:admin",
+ "admin_or_owner": "is_admin:True or project_id:%(project_id)s",
+ "default": "role:admin",
+
+ "add_image": "role:admin",
+ "delete_image": "role:admin",
+ "get_image": "",
+ "get_images": "",
+ "modify_image": "role:admin",
+ "publicize_image": "role:admin",
+ "copy_from": "role:admin",
+
+ "download_image": "",
+ "upload_image": "role:admin",
+
+ "delete_image_location": "role:admin",
+ "get_image_location": "",
+ "set_image_location": "role:admin",
+
+ "add_member": "role:admin",
+ "delete_member": "role:admin",
+ "get_member": "",
+ "get_members": "",
+ "modify_member": "role:admin",
+
+ "manage_image_cache": "role:admin",
+
+ "get_task": "",
+ "get_tasks": "",
+ "add_task": "role:admin",
+ "modify_task": "role:admin",
+
+ "get_metadef_namespace": "",
+ "get_metadef_namespaces":"",
+ "modify_metadef_namespace":"role:admin",
+ "add_metadef_namespace":"role:admin",
+ "delete_metadef_namespace":"role:admin",
+
+ "get_metadef_object":"",
+ "get_metadef_objects":"",
+ "modify_metadef_object":"role:admin",
+ "add_metadef_object":"role:admin",
+
+ "list_metadef_resource_types":"rule:admin_or_owner",
+ "add_metadef_resource_type_association":"",
+
+ "get_metadef_property":"",
+ "get_metadef_properties":"",
+ "modify_metadef_property":"role:admin",
+ "add_metadef_property":"role:admin"
+}
diff --git a/modules/openstack/files/ocata/horizon/keystone_policy.json
b/modules/openstack/files/ocata/horizon/keystone_policy.json
new file mode 100644
index 0000000..0bd9546
--- /dev/null
+++ b/modules/openstack/files/ocata/horizon/keystone_policy.json
@@ -0,0 +1,183 @@
+{
+ "admin_required": "role:admin or is_admin:1",
+ "service_role": "role:service",
+ "service_or_admin": "rule:admin_required or rule:service_role",
+ "owner" : "user_id:%(user_id)s",
+ "admin_or_owner": "rule:admin_required or rule:owner",
+ "token_subject": "user_id:%(target.token.user_id)s",
+ "admin_or_token_subject": "rule:admin_required or rule:token_subject",
+
+ "default": "rule:admin_required",
+
+ "identity:get_region": "",
+ "identity:list_regions": "rule:admin_required",
+ "identity:create_region": "rule:admin_required",
+ "identity:update_region": "rule:admin_required",
+ "identity:delete_region": "rule:admin_required",
+
+ "identity:get_service": "",
+ "identity:list_services": "",
+ "identity:create_service": "rule:admin_required",
+ "identity:update_service": "rule:admin_required",
+ "identity:delete_service": "rule:admin_required",
+
+ "identity:get_endpoint": "",
+ "identity:list_endpoints": "",
+ "identity:create_endpoint": "rule:admin_required",
+ "identity:update_endpoint": "rule:admin_required",
+ "identity:delete_endpoint": "rule:admin_required",
+
+ "identity:get_domain": "rule:admin_required",
+ "identity:list_domains": "rule:admin_required",
+ "identity:create_domain": "rule:admin_required",
+ "identity:update_domain": "rule:admin_required",
+ "identity:delete_domain": "rule:admin_required",
+
+ "identity:get_project": "rule:admin_required",
+ "identity:list_projects": "role:admin_required",
+ "identity:list_user_projects": "",
+ "identity:create_project": "rule:admin_required",
+ "identity:update_project": "rule:admin_required",
+ "identity:delete_project": "rule:admin_required",
+
+ "identity:get_user": "rule:admin_required",
+ "identity:list_users": "rule:admin_required",
+ "identity:create_user": "rule:admin_required",
+ "identity:update_user": "rule:admin_required",
+ "identity:delete_user": "rule:admin_required",
+ "identity:change_password": "rule:admin_or_owner",
+
+ "identity:get_group": "rule:admin_required",
+ "identity:list_groups": "rule:admin_required",
+ "identity:list_groups_for_user": "rule:admin_or_owner",
+ "identity:create_group": "rule:admin_required",
+ "identity:update_group": "rule:admin_required",
+ "identity:delete_group": "rule:admin_required",
+ "identity:list_users_in_group": "rule:admin_required",
+ "identity:remove_user_from_group": "rule:admin_required",
+ "identity:check_user_in_group": "rule:admin_required",
+ "identity:add_user_to_group": "rule:admin_required",
+
+ "identity:get_credential": "rule:admin_required",
+ "identity:list_credentials": "rule:admin_required",
+ "identity:create_credential": "rule:admin_required",
+ "identity:update_credential": "rule:admin_required",
+ "identity:delete_credential": "rule:admin_required",
+
+ "identity:ec2_get_credential": "rule:admin_required or (rule:owner and
user_id:%(target.credential.user_id)s)",
+ "identity:ec2_list_credentials": "rule:admin_or_owner",
+ "identity:ec2_create_credential": "rule:admin_or_owner",
+ "identity:ec2_delete_credential": "rule:admin_required or (rule:owner and
user_id:%(target.credential.user_id)s)",
+
+ "identity:get_role": "",
+ "identity:list_roles": "rule:admin_required",
+ "identity:create_role": "rule:admin_required",
+ "identity:update_role": "rule:admin_required",
+ "identity:delete_role": "rule:admin_required",
+
+ "identity:check_grant": "rule:admin_required",
+ "identity:list_grants": "rule:admin_required",
+ "identity:create_grant": "rule:admin_required",
+ "identity:revoke_grant": "rule:admin_required",
+
+ "identity:list_role_assignments": "rule:admin_required",
+
+ "identity:get_policy": "rule:admin_required",
+ "identity:list_policies": "rule:admin_required",
+ "identity:create_policy": "rule:admin_required",
+ "identity:update_policy": "rule:admin_required",
+ "identity:delete_policy": "rule:admin_required",
+
+ "identity:check_token": "rule:admin_or_token_subject",
+ "identity:validate_token": "rule:service_admin_or_token_subject",
+ "identity:validate_token_head": "rule:service_or_admin",
+ "identity:revocation_list": "rule:service_or_admin",
+ "identity:revoke_token": "rule:admin_or_token_subject",
+
+ "identity:create_trust": "user_id:%(trust.trustor_user_id)s",
+ "identity:list_trusts": "rule:admin_required",
+ "identity:list_roles_for_trust": "",
+ "identity:get_role_for_trust": "",
+ "identity:delete_trust": "",
+
+ "identity:create_consumer": "rule:admin_required",
+ "identity:get_consumer": "rule:admin_required",
+ "identity:list_consumers": "rule:admin_required",
+ "identity:delete_consumer": "rule:admin_required",
+ "identity:update_consumer": "rule:admin_required",
+
+ "identity:authorize_request_token": "rule:admin_required",
+ "identity:list_access_token_roles": "rule:admin_required",
+ "identity:get_access_token_role": "rule:admin_required",
+ "identity:list_access_tokens": "rule:admin_required",
+ "identity:get_access_token": "rule:admin_required",
+ "identity:delete_access_token": "rule:admin_required",
+
+ "identity:list_projects_for_endpoint": "rule:admin_required",
+ "identity:add_endpoint_to_project": "rule:admin_required",
+ "identity:check_endpoint_in_project": "rule:admin_required",
+ "identity:list_endpoints_for_project": "rule:admin_required",
+ "identity:remove_endpoint_from_project": "rule:admin_required",
+
+ "identity:create_endpoint_group": "rule:admin_required",
+ "identity:list_endpoint_groups": "rule:admin_required",
+ "identity:get_endpoint_group": "rule:admin_required",
+ "identity:update_endpoint_group": "rule:admin_required",
+ "identity:delete_endpoint_group": "rule:admin_required",
+ "identity:list_projects_associated_with_endpoint_group":
"rule:admin_required",
+ "identity:list_endpoints_associated_with_endpoint_group":
"rule:admin_required",
+ "identity:get_endpoint_group_in_project": "rule:admin_required",
+ "identity:list_endpoint_groups_for_project": "rule:admin_required",
+ "identity:add_endpoint_group_to_project": "rule:admin_required",
+ "identity:remove_endpoint_group_from_project": "rule:admin_required",
+
+ "identity:create_identity_provider": "rule:admin_required",
+ "identity:list_identity_providers": "rule:admin_required",
+ "identity:get_identity_providers": "rule:admin_required",
+ "identity:update_identity_provider": "rule:admin_required",
+ "identity:delete_identity_provider": "rule:admin_required",
+
+ "identity:create_protocol": "rule:admin_required",
+ "identity:update_protocol": "rule:admin_required",
+ "identity:get_protocol": "rule:admin_required",
+ "identity:list_protocols": "rule:admin_required",
+ "identity:delete_protocol": "rule:admin_required",
+
+ "identity:create_mapping": "rule:admin_required",
+ "identity:get_mapping": "rule:admin_required",
+ "identity:list_mappings": "rule:admin_required",
+ "identity:delete_mapping": "rule:admin_required",
+ "identity:update_mapping": "rule:admin_required",
+
+ "identity:create_service_provider": "rule:admin_required",
+ "identity:list_service_providers": "rule:admin_required",
+ "identity:get_service_provider": "rule:admin_required",
+ "identity:update_service_provider": "rule:admin_required",
+ "identity:delete_service_provider": "rule:admin_required",
+
+ "identity:get_auth_catalog": "",
+ "identity:get_auth_projects": "",
+ "identity:get_auth_domains": "",
+
+ "identity:list_projects_for_groups": "",
+ "identity:list_domains_for_groups": "",
+
+ "identity:list_revoke_events": "",
+
+ "identity:create_policy_association_for_endpoint": "rule:admin_required",
+ "identity:check_policy_association_for_endpoint": "rule:admin_required",
+ "identity:delete_policy_association_for_endpoint": "rule:admin_required",
+ "identity:create_policy_association_for_service": "rule:admin_required",
+ "identity:check_policy_association_for_service": "rule:admin_required",
+ "identity:delete_policy_association_for_service": "rule:admin_required",
+ "identity:create_policy_association_for_region_and_service":
"rule:admin_required",
+ "identity:check_policy_association_for_region_and_service":
"rule:admin_required",
+ "identity:delete_policy_association_for_region_and_service":
"rule:admin_required",
+ "identity:get_policy_for_endpoint": "rule:admin_required",
+ "identity:list_endpoints_for_policy": "rule:admin_required",
+
+ "identity:create_domain_config": "rule:admin_required",
+ "identity:get_domain_config": "rule:admin_required",
+ "identity:update_domain_config": "rule:admin_required",
+ "identity:delete_domain_config": "rule:admin_required"
+}
diff --git a/modules/openstack/files/ocata/horizon/nova_policy.json
b/modules/openstack/files/ocata/horizon/nova_policy.json
new file mode 100644
index 0000000..92ff69d
--- /dev/null
+++ b/modules/openstack/files/ocata/horizon/nova_policy.json
@@ -0,0 +1,138 @@
+{
+ "context_is_admin": [["role:admin"]],
+ "admin_or_member": [["is_admin:True"], ["project_id:%(project_id)s"]],
+ "admin_or_projectadmin": [["is_admin:True"], ["role:projectadmin"]],
+ "default": [["rule:admin_or_projectadmin"]],
+
+ "compute:create": "rule:admin_or_projectadmin",
+ "compute:delete": "rule:admin_or_projectadmin",
+ "compute:create:attach_network": "role:admin",
+ "compute:create:attach_volume": "role:admin",
+ "compute:rebuild": "!",
+ "compute:start": "rule:admin_or_projectadmin",
+ "compute:stop": "rule:admin_or_projectadmin",
+ "compute:snapshot": "!",
+ "compute:get_all": [],
+ "compute:get_console_output": "rule:admin_or_member",
+
+ "sudorule:list": "",
+ "sudorule:delete": "rule:admin_or_projectadmin",
+ "sudorule:create": "rule:admin_or_projectadmin",
+ "sudorule:modify": "rule:admin_or_projectadmin",
+
+ "admin_api": [["is_admin:True"]],
+ "compute_extension:accounts": [["rule:admin_api"]],
+ "compute_extension:admin_actions": [["rule:admin_api"]],
+ "compute_extension:admin_actions:pause": [["rule:admin_or_projectadmin"]],
+ "compute_extension:admin_actions:unpause":
[["rule:admin_or_projectadmin"]],
+ "compute_extension:admin_actions:suspend": "!",
+ "compute_extension:shelve": "!",
+ "compute_extension:admin_actions:resume": [["rule:admin_or_projectadmin"]],
+ "compute_extension:admin_actions:lock": [["rule:admin_api"]],
+ "compute_extension:admin_actions:unlock": [["rule:admin_api"]],
+ "compute_extension:admin_actions:resetNetwork": [["rule:admin_api"]],
+ "compute_extension:admin_actions:injectNetworkInfo": [["rule:admin_api"]],
+ "compute_extension:admin_actions:createBackup": [["role:admin"]],
+ "compute_extension:admin_actions:migrateLive": [["rule:admin_api"]],
+ "compute_extension:admin_actions:resetState": [["rule:admin_api"]],
+ "compute_extension:admin_actions:migrate": [["rule:admin_api"]],
+ "compute_extension:aggregates": [["rule:admin_api"]],
+ "compute_extension:certificates": [],
+ "compute_extension:cloudpipe": [["rule:admin_api"]],
+ "compute_extension:console_output": [["rule:admin_or_projectadmin"]],
+ "compute_extension:consoles": [["rule:admin_api"]],
+ "compute_extension:createserverext": [["rule:admin_or_projectadmin"]],
+ "compute_extension:deferred_delete": [["rule:admin_or_projectadmin"]],
+ "compute_extension:disk_config": [["rule:admin_or_projectadmin"]],
+ "compute_extension:extended_server_attributes": [],
+ "compute_extension:extended_status": [],
+ "compute_extension:flavor_access": [],
+ "compute_extension:flavor_disabled": [],
+ "compute_extension:flavor_rxtx": [],
+ "compute_extension:flavor_swap": [],
+ "compute_extension:flavorextradata": [],
+ "compute_extension:flavorextraspecs": [],
+ "compute_extension:flavormanage": [["rule:admin_api"]],
+ "compute_extension:floating_ip_dns": [["rule:admin_or_projectadmin"]],
+ "compute_extension:floating_ip_pools": [["rule:admin_or_projectadmin"]],
+ "compute_extension:floating_ips": [["rule:admin_or_projectadmin"]],
+ "compute_extension:hosts": [["rule:admin_api"]],
+ "compute_extension:hypervisors": [["rule:admin_api"]],
+ "compute_extension:instance_usage_audit_log": [["rule:admin_api"]],
+ "compute_extension:keypairs": "!",
+ "compute_extension:keypairs:create": "!",
+ "compute_extension:multinic": [["rule:admin_or_projectadmin"]],
+ "compute_extension:networks": [],
+ "compute_extension:networks:view": [],
+ "compute_extension:quotas:show": [["rule:admin_or_member"]],
+ "compute_extension:quotas:update": [["rule:admin_api"]],
+ "compute_extension:quota_classes": [["rule:admin_or_projectadmin"]],
+ "compute_extension:rescue": [["rule:admin_or_projectadmin"]],
+ "compute_extension:security_groups": [["rule:admin_or_projectadmin"]],
+ "compute_extension:server_diagnostics": [["rule:admin_api"]],
+ "compute_extension:simple_tenant_usage:show": [["rule:admin_or_member"]],
+ "compute_extension:simple_tenant_usage:list": [["rule:admin_api"]],
+ "compute_extension:users": [["rule:admin_api"]],
+ "compute_extension:virtual_interfaces": [["rule:admin_or_projectadmin"]],
+ "compute_extension:virtual_storage_arrays":
[["rule:admin_or_projectadmin"]],
+ "compute_extension:volumes": [["rule:admin_or_projectadmin"]],
+ "compute_extension:volumetypes": [["rule:admin_or_projectadmin"]],
+
+ "compute:volume_snapshot_create": "!",
+ "compute:volume_snapshot_delete": "!",
+ "compute:resize": "!",
+ "compute:confirm_resize": "!",
+ "compute:revert_resize": "!",
+
+ "volume:create": [["rule:admin_or_projectadmin"]],
+ "volume:get_all": [],
+ "volume:get_volume_metadata": [],
+ "volume:get_snapshot": "!",
+ "volume:get_all_snapshots": "!",
+ "volume:create_snapshot": "!",
+
+
+ "volume_extension:types_manage": [["rule:admin_api"]],
+ "volume_extension:types_extra_specs": [["rule:admin_api"]],
+ "volume_extension:volume_admin_actions:reset_status": [["rule:admin_api"]],
+ "volume_extension:snapshot_admin_actions:reset_status":
[["rule:admin_api"]],
+ "volume_extension:volume_admin_actions:force_delete": [["rule:admin_api"]],
+
+
+ "network:get_all_networks": [],
+ "network:get_network": [],
+ "network:delete_network": [["rule:admin_api"]],
+ "network:disassociate_network": [["rule:admin_or_projectadmin"]],
+ "network:get_vifs_by_instance": [],
+ "network:allocate_for_instance": [["rule:admin_or_projectadmin"]],
+ "network:deallocate_for_instance": [["rule:admin_or_projectadmin"]],
+ "network:validate_networks": [],
+ "network:get_instance_uuids_by_ip_filter": [],
+
+ "network:get_floating_ip": [],
+ "network:get_floating_ip_pools": [],
+ "network:get_floating_ip_by_address": [],
+ "network:get_floating_ips_by_project": [],
+ "network:get_floating_ips_by_fixed_address": [],
+ "network:allocate_floating_ip": [["rule:admin_or_projectadmin"]],
+ "network:deallocate_floating_ip": [["rule:admin_or_projectadmin"]],
+ "network:associate_floating_ip": [["rule:admin_or_projectadmin"]],
+ "network:disassociate_floating_ip": [["rule:admin_or_projectadmin"]],
+
+ "network:get_fixed_ip": [],
+ "network:get_fixed_ip_by_address": [],
+ "network:add_fixed_ip_to_instance": [["rule:admin_or_projectadmin"]],
+ "network:remove_fixed_ip_from_instance": [["rule:admin_or_projectadmin"]],
+ "network:add_network_to_project": [["rule:admin_or_projectadmin"]],
+ "network:get_instance_nw_info": [],
+
+ "network:get_dns_domains": [],
+ "network:add_dns_entry": [["rule:admin_or_projectadmin"]],
+ "network:modify_dns_entry": [["rule:admin_or_projectadmin"]],
+ "network:delete_dns_entry": [["rule:admin_or_projectadmin"]],
+ "network:get_dns_entries_by_address": [],
+ "network:get_dns_entries_by_name": [],
+ "network:create_private_dns_domain": [["rule:admin_or_projectadmin"]],
+ "network:create_public_dns_domain": [["rule:admin_or_projectadmin"]],
+ "network:delete_dns_domain": [["rule:admin_or_projectadmin"]]
+}
diff --git a/modules/openstack/manifests/horizon/source_deploy.pp
b/modules/openstack/manifests/horizon/source_deploy.pp
new file mode 100644
index 0000000..ecb285c
--- /dev/null
+++ b/modules/openstack/manifests/horizon/source_deploy.pp
@@ -0,0 +1,95 @@
+# The OpenStack Das2hboard Project
+# http://docs.openstack.org/developer/horizon/
+class openstack::horizon::service(
+ $version,
+ $nova_controller,
+ $wmflabsdotorg_admin,
+ $wmflabsdotorg_pass,
+ $dhcp_domain,
+ $ldap_user_pass,
+ $deploy_dir = '/srv/deployment/horizon/deploy',
+ $venv_dir = '/srv/deployment/horizon/venv',
+ $webserver_hostname = 'newhorizon.wikimedia.org'
+) {
+
+ include ::apache
+ include ::apache::mod::ssl
+ include ::apache::mod::wsgi
+ include ::apache::mod::rewrite
+ include ::apache::mod::headers
+ include ::memcached
+
+ require_package([
+ 'python-wheel',
+ 'python-virtualenv',
+ ]
+ )
+
+ file { '/etc/openstack-dashboard/local_settings.py':
+ content =>
template("openstack/${version}/horizon/local_settings.py.erb"),
+ owner => 'horizon',
+ group => 'horizon',
+ mode => '0440',
+ require => Package['openstack-dashboard'],
+ notify => [Service['apache2'], Exec['djangorefresh']],
+ }
+
+ # In the perfect future, Horizon policies will be the same
+ # files that the respective services use. In the meantime, though
+ # it's useful to be able to disable not-yet-supported horizon features.
+ file { '/etc/openstack-dashboard/nova_policy.json':
+ source =>
"puppet:///modules/openstack/${version}/horizon/nova_policy.json",
+ owner => 'horizon',
+ group => 'horizon',
+ mode => '0440',
+ require => Package['openstack-dashboard'],
+ notify => Service['apache2'],
+ }
+ file { '/etc/openstack-dashboard/glance_policy.json':
+ source =>
"puppet:///modules/openstack/${version}/horizon/glance_policy.json",
+ owner => 'horizon',
+ group => 'horizon',
+ mode => '0440',
+ require => Package['openstack-dashboard'],
+ notify => Service['apache2'],
+ }
+
+ # We need a horizon-specific keystone policy because horizon does
weird/special
+ # things for admin_required policies which I don't totally understand.
In particular,
+ # some permissive policies here (e.g. "") cause Horizon to panic, not ask
Keystone for permission,
+ # and log out the user.
+ file { '/etc/openstack-dashboard/keystone_policy.json':
+ source =>
"puppet:///modules/openstack/${version}/horizon/keystone_policy.json",
+ owner => 'horizon',
+ group => 'horizon',
+ mode => '0440',
+ require => Package['openstack-dashboard'],
+ notify => Service['apache2'],
+ }
+
+ file { '/etc/openstack-dashboard/designate_policy.json':
+ source =>
"puppet:///modules/openstack/${version}/designate/policy.json",
+ owner => 'horizon',
+ group => 'horizon',
+ mode => '0440',
+ require => Package['openstack-dashboard'],
+ notify => Service['apache2'],
+ }
+
+ # This is a trivial policy file that forbids everything. We'll use it
+ # for services that we don't support to prevent Horizon from
+ # displaying spurious panels.
+ file { '/etc/openstack-dashboard/disabled_policy.json':
+ source =>
"puppet:///modules/openstack/${version}/horizon/disabled_policy.json",
+ owner => 'horizon',
+ group => 'horizon',
+ mode => '0440',
+ require => Package['openstack-dashboard'],
+ notify => Service['apache2'],
+ }
+
+ apache::site { $webserver_hostname:
+ content =>
template("openstack/${version}/horizon/${webserver_hostname}.erb"),
+ require => File['/etc/openstack-dashboard/local_settings.py'],
+ }
+}
diff --git
a/modules/openstack/templates/ocata/horizon/labtesthorizon.wikimedia.org.erb
b/modules/openstack/templates/ocata/horizon/labtesthorizon.wikimedia.org.erb
new file mode 100644
index 0000000..2f87666
--- /dev/null
+++ b/modules/openstack/templates/ocata/horizon/labtesthorizon.wikimedia.org.erb
@@ -0,0 +1,23 @@
+#####################################################################
+### THIS FILE IS MANAGED BY PUPPET
+#####################################################################
+# vim: filetype=apache
+
+# This config is behind misc-web which handles ssl for us.
+<VirtualHost *:80>
+ ServerName <%= @webserver_hostname %>
+
+ RewriteEngine on
+ RewriteCond %{HTTP:X-Forwarded-Proto} !https
+ RewriteCond %{REQUEST_URI} !^/status$
+ RewriteRule ^/(.*)$ https://%{HTTP_HOST}%{REQUEST_URI}
[R=301,E=ProtoRedirect]
+ Header always merge Vary X-Forwarded-Proto env=ProtoRedirect
+
+ WSGIScriptAlias /
/usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi
+ WSGIDaemonProcess horizon user=horizon group=horizon processes=3 threads=10
+ WSGIProcessGroup horizon
+ Alias /static /usr/share/openstack-dashboard/openstack_dashboard/static/
+ <Directory /usr/share/openstack-dashboard/openstack_dashboard/wsgi>
+ Require all granted
+ </Directory>
+</VirtualHost>
diff --git a/modules/openstack/templates/ocata/horizon/local_settings.py.erb
b/modules/openstack/templates/ocata/horizon/local_settings.py.erb
new file mode 100644
index 0000000..1c0681b
--- /dev/null
+++ b/modules/openstack/templates/ocata/horizon/local_settings.py.erb
@@ -0,0 +1,549 @@
+#####################################################################
+### THIS FILE IS MANAGED BY PUPPET
+#####################################################################
+import os
+
+from django.utils.translation import ugettext_lazy as _
+
+from openstack_dashboard import exceptions
+
+DEBUG = False
+TEMPLATE_DEBUG = DEBUG
+
+# Required for Django 1.5.
+# If horizon is running in production (DEBUG is False), set this
+# with the list of host/domain names that the application can serve.
+# For more information see:
+# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
+ALLOWED_HOSTS = ['horizon.wikimedia.org', ]
+
+AUTHENTICATION_PLUGINS = ['openstack_auth.plugin.wmtotp.WmtotpPlugin',
'openstack_auth.plugin.token.TokenPlugin']
+
+# Set SSL proxy settings:
+# For Django 1.4+ pass this header from the proxy after terminating the SSL,
+# and don't forget to strip it from the client's request.
+# For more information see:
+# https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header
+# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
+
+# SESSION_TIMEOUT is in seconds and defaults to 1800. Change to 7 days
+# by default, and also support SESSION_SHORT_TIMEOUT of 30 minutes.
+#
+# SESSION_SHORT is a WMF hack. SESSION_TIMEOUT is used in multiple
+# places as a maximum in Horizon code, so it was easier to add
+# a special case extra-short token for when the user does not
+# check 'Remember me'.
+SESSION_SHORT_TIMEOUT = 1800
+SESSION_TIMEOUT = 604800
+SESSION_COOKIE_AGE = 604800
+CSRF_COOKIE_SECURE = True
+SESSION_COOKIE_SECURE = True
+
+# Overrides for OpenStack API versions. Use this setting to force the
+# OpenStack dashboard to use a specific API version for a given service API.
+# NOTE: The version should be formatted as it appears in the URL for the
+# service API. For example, The identity service APIs have inconsistent
+# use of the decimal point, so valid options would be "2.0" or "3".
+# OPENSTACK_API_VERSIONS = {
+# "identity": 3,
+# "volume": 2
+# }
+
+OPENSTACK_API_VERSIONS = {
+ "identity": 3,
+}
+
+# Set this to True if running on multi-domain model. When this is enabled, it
+# will require user to enter the Domain name in addition to username for login.
+# OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = False
+
+# Overrides the default domain used when running on single-domain model
+# with Keystone V3. All entities will be created in the default domain.
+# OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'Default'
+
+# Set Console type:
+# valid options would be "AUTO", "VNC", "SPICE" or "RDP"
+# CONSOLE_TYPE = "AUTO"
+
+# Default OpenStack Dashboard configuration.
+HORIZON_CONFIG = {
+ 'user_home': 'openstack_dashboard.views.get_user_home',
+ 'ajax_queue_limit': 10,
+ 'auto_fade_alerts': {
+ 'delay': 3000,
+ 'fade_duration': 1500,
+ 'types': ['alert-success', 'alert-info']
+ },
+ 'help_url': "https://wikitech.wikimedia.org/wiki/Horizon",
+ 'exceptions': {'recoverable': exceptions.RECOVERABLE,
+ 'not_found': exceptions.NOT_FOUND,
+ 'unauthorized': exceptions.UNAUTHORIZED},
+ 'customization_module': 'horizon.overrides',
+}
+
+# Specify a regular expression to validate user passwords.
+# HORIZON_CONFIG["password_validator"] = {
+# "regex": '.*',
+# "help_text": _("Your password does not meet the requirements.")
+# }
+
+# Disable simplified floating IP address management for deployments with
+# multiple floating IP pools or complex network requirements.
+# HORIZON_CONFIG["simple_ip_management"] = False
+
+# Turn on browser autocompletion for the login form
+HORIZON_CONFIG["password_autocomplete"] = "on"
+
+LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
+
+# Set custom secret key:
+# You can either set it to a specific value or you can let horizion generate a
+# default secret key that is unique on this machine, e.i. regardless of the
+# amount of Python WSGI workers (if used behind Apache+mod_wsgi): However,
there
+# may be situations where you would want to set this explicitly, e.g. when
+# multiple dashboard instances are distributed on different machines (usually
+# behind a load-balancer). Either you have to make sure that a session gets all
+# requests routed to the same dashboard instance or you set the same SECRET_KEY
+# for all of them.
+from horizon.utils import secret_key
+SECRET_KEY =
secret_key.generate_or_read_from_file('/var/lib/openstack-dashboard/secret_key')
+
+# We recommend you use memcached for development; otherwise after every reload
+# of the django development server, you will have to login again.
+SESSION_ENGINE='django.contrib.sessions.backends.cache'
+CACHES = {
+ 'default': {
+ 'BACKEND' : 'django.core.cache.backends.memcached.MemcachedCache',
+ 'LOCATION' : '127.0.0.1:11000',
+ }
+}
+
+# Enable the Ubuntu theme if it is present.
+try:
+ from ubuntu_theme import *
+except ImportError:
+ pass
+
+# Default Ubuntu apache configuration uses /horizon as the application root.
+# Configure auth redirects here accordingly.
+LOGIN_URL='/auth/login/'
+LOGOUT_URL='/auth/logout/'
+LOGIN_REDIRECT_URL='/'
+
+# The Ubuntu package includes pre-compressed JS and compiled CSS to allow
+# offline compression by default. To enable online compression, install
+# the node-less package and enable the following option.
+COMPRESS_OFFLINE = True
+COMPRESS_ENABLED = True
+
+# By default, validation of the HTTP Host header is disabled. Production
+# installations should have this set accordingly. For more information
+# see https://docs.djangoproject.com/en/dev/ref/settings/.
+ALLOWED_HOSTS = '*'
+
+# Send email to the console by default
+EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+# Or send them to /dev/null
+#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
+
+# Configure these for your outgoing email host
+# EMAIL_HOST = 'smtp.my-company.com'
+# EMAIL_PORT = 25
+# EMAIL_HOST_USER = 'djangomail'
+# EMAIL_HOST_PASSWORD = 'top-secret!'
+
+# For multiple regions uncomment this configuration, and add (endpoint, title).
+# AVAILABLE_REGIONS = [
+# ('http://cluster1.example.com:5000/v2.0', 'cluster1'),
+# ('http://cluster2.example.com:5000/v2.0', 'cluster2'),
+# ]
+
+OPENSTACK_HOST = "<%= @nova_controller %>"
+OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
+OPENSTACK_KEYSTONE_DEFAULT_ROLE = "user"
+
+# Disable SSL certificate checks (useful for self-signed certificates):
+# OPENSTACK_SSL_NO_VERIFY = True
+
+# The CA certificate to use to verify SSL connections
+# OPENSTACK_SSL_CACERT = '/path/to/cacert.pem'
+
+# The OPENSTACK_KEYSTONE_BACKEND settings can be used to identify the
+# capabilities of the auth backend for Keystone.
+# If Keystone has been configured to use LDAP as the auth backend then set
+# can_edit_user to False and name to 'ldap'.
+#
+# TODO(tres): Remove these once Keystone has an API to identify auth backend.
+OPENSTACK_KEYSTONE_BACKEND = {
+ 'name': 'native',
+ 'can_edit_user': True,
+ 'can_edit_group': True,
+ 'can_edit_project': True,
+ 'can_edit_domain': True,
+ 'can_edit_role': True
+}
+
+#Setting this to True, will add a new "Retrieve Password" action on instance,
+#allowing Admin session password retrieval/decryption.
+#OPENSTACK_ENABLE_PASSWORD_RETRIEVE = False
+
+# The Xen Hypervisor has the ability to set the mount point for volumes
+# attached to instances (other Hypervisors currently do not). Setting
+# can_set_mount_point to True will add the option to set the mount point
+# from the UI.
+OPENSTACK_HYPERVISOR_FEATURES = {
+ 'can_set_mount_point': False,
+ 'can_set_password': False,
+}
+
+# The OPENSTACK_NEUTRON_NETWORK settings can be used to enable optional
+# services provided by neutron. Options currently available are load
+# balancer service, security groups, quotas, VPN service.
+OPENSTACK_NEUTRON_NETWORK = {
+ 'enable_lb': False,
+ 'enable_firewall': False,
+ 'enable_quotas': True,
+ 'enable_vpn': False,
+ # The profile_support option is used to detect if an external router can be
+ # configured via the dashboard. When using specific plugins the
+ # profile_support can be turned on if needed.
+ 'profile_support': None,
+ #'profile_support': 'cisco',
+}
+
+# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features
+# in the OpenStack Dashboard related to the Image service, such as the list
+# of supported image formats.
+# OPENSTACK_IMAGE_BACKEND = {
+# 'image_formats': [
+# ('', ''),
+# ('aki', _('AKI - Amazon Kernel Image')),
+# ('ami', _('AMI - Amazon Machine Image')),
+# ('ari', _('ARI - Amazon Ramdisk Image')),
+# ('iso', _('ISO - Optical Disk Image')),
+# ('qcow2', _('QCOW2 - QEMU Emulator')),
+# ('raw', _('Raw')),
+# ('vdi', _('VDI')),
+# ('vhd', _('VHD')),
+# ('vmdk', _('VMDK'))
+# ]
+# }
+
+# The IMAGE_CUSTOM_PROPERTY_TITLES settings is used to customize the titles for
+# image custom property attributes that appear on image detail pages.
+IMAGE_CUSTOM_PROPERTY_TITLES = {
+ "architecture": _("Architecture"),
+ "kernel_id": _("Kernel ID"),
+ "ramdisk_id": _("Ramdisk ID"),
+ "image_state": _("Euca2ools state"),
+ "project_id": _("Project ID"),
+ "image_type": _("Image Type")
+}
+
+# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints
+# in the Keystone service catalog. Use this setting when Horizon is running
+# external to the OpenStack environment. The default is 'publicURL'.
+#OPENSTACK_ENDPOINT_TYPE = "publicURL"
+
+# SECONDARY_ENDPOINT_TYPE specifies the fallback endpoint type to use in the
+# case that OPENSTACK_ENDPOINT_TYPE is not present in the endpoints
+# in the Keystone service catalog. Use this setting when Horizon is running
+# external to the OpenStack environment. The default is None. This
+# value should differ from OPENSTACK_ENDPOINT_TYPE if used.
+#SECONDARY_ENDPOINT_TYPE = "publicURL"
+
+# The number of objects (Swift containers/objects or images) to display
+# on a single page before providing a paging element (a "more" link)
+# to paginate results.
+API_RESULT_LIMIT = 1000
+API_RESULT_PAGE_SIZE = 20
+
+# The timezone of the server. This should correspond with the timezone
+# of your entire OpenStack installation, and hopefully be in UTC.
+TIME_ZONE = "UTC"
+
+# When launching an instance, the menu of available flavors is
+# sorted by RAM usage, ascending. If you would like a different sort order,
+# you can provide another flavor attribute as sorting key. Alternatively, you
+# can provide a custom callback method to use for sorting. You can also provide
+# a flag for reverse sort. For more info, see
+# http://docs.python.org/2/library/functions.html#sorted
+# CREATE_INSTANCE_FLAVOR_SORT = {
+# 'key': 'name',
+# # or
+# 'key': my_awesome_callback_method,
+# 'reverse': False,
+# }
+
+# The Horizon Policy Enforcement engine uses these values to load per service
+# policy rule files. The content of these files should match the files the
+# OpenStack services are using to determine role based access control in the
+# target installation.
+
+# Path to directory containing policy.json files
+POLICY_FILES_PATH = '/etc/openstack-dashboard/'
+# Map of local copy of service policy files
+POLICY_FILES = {
+ 'identity': 'keystone_policy.json',
+ 'compute': 'nova_policy.json',
+ 'image': 'glance_policy.json',
+ 'dns': 'designate_policy.json',
+ 'volume': 'disabled_policy.json',
+ 'telemetry': 'disabled_policy.json',
+ 'orchestration': 'disabled_policy.json',
+ 'network': 'disabled_policy.json',
+}
+
+# Trove user and database extension support. By default support for
+# creating users and databases on database instances is turned on.
+# To disable these extensions set the permission here to something
+# unusable such as ["!"].
+# TROVE_ADD_USER_PERMS = []
+# TROVE_ADD_DATABASE_PERMS = []
+
+LOGGING = {
+ 'version': 1,
+ # When set to True this will disable all logging except
+ # for loggers specified in this configuration dictionary. Note that
+ # if nothing is specified here and disable_existing_loggers is True,
+ # django.db.backends will still log unless it is disabled explicitly.
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'null': {
+ 'level': 'DEBUG',
+ 'class': 'django.utils.log.NullHandler',
+ },
+ 'console': {
+ # Set the level to "DEBUG" for verbose output logging.
+ 'level': 'INFO',
+ 'class': 'logging.StreamHandler',
+ },
+ },
+ 'loggers': {
+ # Logging from django.db.backends is VERY verbose, send to null
+ # by default.
+ 'django.db.backends': {
+ 'handlers': ['null'],
+ 'propagate': False,
+ },
+ 'requests': {
+ 'handlers': ['null'],
+ 'propagate': False,
+ },
+ 'horizon': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'openstack_dashboard': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'novaclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'cinderclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'keystoneclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'glanceclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'neutronclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'heatclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'ceilometerclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'troveclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'swiftclient': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'openstack_auth': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'nose.plugins.manager': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'django': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'iso8601': {
+ 'handlers': ['null'],
+ 'propagate': False,
+ },
+ }
+}
+
+# 'direction' should not be specified for all_tcp/udp/icmp.
+# It is specified in the form.
+SECURITY_GROUP_RULES = {
+ 'all_tcp': {
+ 'name': 'ALL TCP',
+ 'ip_protocol': 'tcp',
+ 'from_port': '1',
+ 'to_port': '65535',
+ },
+ 'all_udp': {
+ 'name': 'ALL UDP',
+ 'ip_protocol': 'udp',
+ 'from_port': '1',
+ 'to_port': '65535',
+ },
+ 'all_icmp': {
+ 'name': 'ALL ICMP',
+ 'ip_protocol': 'icmp',
+ 'from_port': '-1',
+ 'to_port': '-1',
+ },
+ 'ssh': {
+ 'name': 'SSH',
+ 'ip_protocol': 'tcp',
+ 'from_port': '22',
+ 'to_port': '22',
+ },
+ 'smtp': {
+ 'name': 'SMTP',
+ 'ip_protocol': 'tcp',
+ 'from_port': '25',
+ 'to_port': '25',
+ },
+ 'dns': {
+ 'name': 'DNS',
+ 'ip_protocol': 'tcp',
+ 'from_port': '53',
+ 'to_port': '53',
+ },
+ 'http': {
+ 'name': 'HTTP',
+ 'ip_protocol': 'tcp',
+ 'from_port': '80',
+ 'to_port': '80',
+ },
+ 'pop3': {
+ 'name': 'POP3',
+ 'ip_protocol': 'tcp',
+ 'from_port': '110',
+ 'to_port': '110',
+ },
+ 'imap': {
+ 'name': 'IMAP',
+ 'ip_protocol': 'tcp',
+ 'from_port': '143',
+ 'to_port': '143',
+ },
+ 'ldap': {
+ 'name': 'LDAP',
+ 'ip_protocol': 'tcp',
+ 'from_port': '389',
+ 'to_port': '389',
+ },
+ 'https': {
+ 'name': 'HTTPS',
+ 'ip_protocol': 'tcp',
+ 'from_port': '443',
+ 'to_port': '443',
+ },
+ 'smtps': {
+ 'name': 'SMTPS',
+ 'ip_protocol': 'tcp',
+ 'from_port': '465',
+ 'to_port': '465',
+ },
+ 'imaps': {
+ 'name': 'IMAPS',
+ 'ip_protocol': 'tcp',
+ 'from_port': '993',
+ 'to_port': '993',
+ },
+ 'pop3s': {
+ 'name': 'POP3S',
+ 'ip_protocol': 'tcp',
+ 'from_port': '995',
+ 'to_port': '995',
+ },
+ 'ms_sql': {
+ 'name': 'MS SQL',
+ 'ip_protocol': 'tcp',
+ 'from_port': '1433',
+ 'to_port': '1433',
+ },
+ 'mysql': {
+ 'name': 'MYSQL',
+ 'ip_protocol': 'tcp',
+ 'from_port': '3306',
+ 'to_port': '3306',
+ },
+ 'rdp': {
+ 'name': 'RDP',
+ 'ip_protocol': 'tcp',
+ 'from_port': '3389',
+ 'to_port': '3389',
+ },
+}
+
+FLAVOR_EXTRA_KEYS = {
+ 'flavor_keys': [
+ ('quota:read_bytes_sec', _('Quota: Read bytes')),
+ ('quota:write_bytes_sec', _('Quota: Write bytes')),
+ ('quota:cpu_quota', _('Quota: CPU')),
+ ('quota:cpu_period', _('Quota: CPU period')),
+ ('quota:inbound_average', _('Quota: Inbound average')),
+ ('quota:outbound_average', _('Quota: Outbound average')),
+ ]
+}
+
+# WMF-specific branding
+SITE_BRANDING = 'Manage Wikimedia Labs'
+SITE_BRANDING_LINK = 'https://horizon.wikimedia.org'
+
+# WMF-specific security limits
+HORIZON_IMAGES_ALLOW_UPLOAD = False
+
+DESIGNATE = { 'records_use_fips': True }
+
+WMFLABSDOTORG_ADMIN_USERNAME = '<%= @wmflabsdotorg_admin %>'
+WMFLABSDOTORG_ADMIN_PASSWORD = '<%= @wmflabsdotorg_pass %>'
+INSTANCE_TLD = '<%= @dhcp_domain %>'
+
+# Disable instance consoles for Horizon users
+CONSOLE_TYPE = False
+
+PUPPETMASTER_API = "https://<%= scope.call_function(:hiera,
['labs_puppet_master']) %>:8140/future"
+PUPPET_CONFIG_BACKEND = "http://<%= scope.call_function(:hiera,
['labs_puppet_master']) %>:8101/v1"
+
+LDAP_USER = "uid=novaadmin,ou=people,dc=wikimedia,dc=org"
+LDAP_PROJECTS_BASE = "ou=projects,dc=wikimedia,dc=org"
+LDAP_USER_PASSWORD = "<%= @ldap_user_pass %>"
diff --git
a/modules/openstack/templates/ocata/horizon/newhorizon.wikimedia.org.erb
b/modules/openstack/templates/ocata/horizon/newhorizon.wikimedia.org.erb
new file mode 100644
index 0000000..c8c3ed2
--- /dev/null
+++ b/modules/openstack/templates/ocata/horizon/newhorizon.wikimedia.org.erb
@@ -0,0 +1,23 @@
+#####################################################################
+### THIS FILE IS MANAGED BY PUPPET
+#####################################################################
+# vim: filetype=apache
+
+# This config is behind misc-web which handles ssl for us.
+<VirtualHost *:80>
+ ServerName <%= @webserver_hostname %>
+
+ RewriteEngine on
+ RewriteCond %{HTTP:X-Forwarded-Proto} !https
+ RewriteCond %{REQUEST_URI} !^/status$
+ RewriteRule ^/(.*)$ https://%{HTTP_HOST}%{REQUEST_URI}
[R=301,E=ProtoRedirect]
+ Header always merge Vary X-Forwarded-Proto env=ProtoRedirect
+
+ WSGIScriptAlias / <%= @venv_dir
%>/usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi
+ WSGIDaemonProcess horizon user=horizon group=horizon processes=3 threads=10
+ WSGIProcessGroup horizon
+ Alias /static <%= @venv_dir
%>/usr/share/openstack-dashboard/openstack_dashboard/static/
+ <Directory <%= @venv_dir
%>/usr/share/openstack-dashboard/openstack_dashboard/wsgi>
+ Require all granted
+ </Directory>
+</VirtualHost>
--
To view, visit https://gerrit.wikimedia.org/r/405373
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie3cc3df3b48a4933672f98d574551768e4f82314
Gerrit-PatchSet: 1
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Andrew Bogott <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits