@@ -0,0 +1,245 @@
1+{ config, lib, pkgs, ... }:
2+
3+with lib; with import ./common.nix {inherit lib;};
4+
5+let
6+ cfg = config.virtualisation.openstack.glance;
7+ commonConf = ''
8+ [database]
9+ connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
10+ notification_driver = noop
11+
12+ [keystone_authtoken]
13+ auth_url = ${cfg.authUrl}
14+ auth_plugin = password
15+ project_name = service
16+ project_domain_id = default
17+ user_domain_id = default
18+ username = ${cfg.serviceUsername}
19+ password = ${cfg.servicePassword.pattern}
20+
21+ [glance_store]
22+ default_store = file
23+ filesystem_store_datadir = /var/lib/glance/images/
24+ '';
25+ glanceApiConfTpl = pkgs.writeText "glance-api.conf" ''
26+ ${commonConf}
27+
28+ [paste_deploy]
29+ flavor = keystone
30+ config_file = ${cfg.package}/etc/glance-api-paste.ini
31+ '';
32+ glanceRegistryConfTpl = pkgs.writeText "glance-registry.conf" ''
33+ ${commonConf}
34+
35+ [paste_deploy]
36+ config_file = ${cfg.package}/etc/glance-registry-paste.ini
37+ '';
38+ glanceApiConf = "/var/lib/glance/glance-api.conf";
39+ glanceRegistryConf = "/var/lib/glance/glance-registry.conf";
40+
41+in {
42+ options.virtualisation.openstack.glance = {
43+ package = mkOption {
44+ type = types.package;
45+ default = pkgs.glance;
46+ example = literalExample "pkgs.glance";
47+ description = ''
48+ Glance package to use.
49+ '';
50+ };
51+
52+ enable = mkOption {
53+ default = false;
54+ type = types.bool;
55+ description = ''
56+ This option enables Glance as a single-machine
57+ installation. That is, all of Glance's components are
58+ enabled on this machine. This is useful for evaluating and
59+ experimenting with Glance. Note we are currently not
60+ providing any configurations for a multi-node setup.
61+ '';
62+ };
63+
64+ authUrl = mkOption {
65+ type = types.str;
66+ default = http://localhost:5000;
67+ description = ''
68+ Complete public Identity (Keystone) API endpoint. Note this is
69+ unversionned.
70+ '';
71+ };
72+
73+ serviceUsername = mkOption {
74+ type = types.str;
75+ default = "glance";
76+ description = ''
77+ The Glance service username. This user is created if bootstrap
78+ is enable, otherwise it has to be manually created before
79+ starting this service.
80+ '';
81+ };
82+
83+ servicePassword = mkSecretOption {
84+ name = "glanceAdminPassword";
85+ description = ''
86+ The Glance service user's password.
87+ '';
88+ };
89+
90+ database = databaseOption "glance";
91+
92+ bootstrap = {
93+ enable = mkOption {
94+ default = false;
95+ type = types.bool;
96+ description = ''
97+ Bootstrap the Glance service by creating the service tenant,
98+ an admin account and a public endpoint. This option provides
99+ a ready-to-use glance service. This is only done at the
100+ first Glance execution by the systemd post start section.
101+ The keystone admin account is used to create required
102+ Keystone resource for the Glance service.
103+
104+ <note><para> This option is a helper for setting up
105+ development or testing environments.</para></note>
106+ '';
107+ };
108+
109+ endpointPublic = mkOption {
110+ type = types.str;
111+ default = "http://localhost:9292";
112+ description = ''
113+ The public image endpoint. The link <link
114+ xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
115+ create endpoint</link> provides more informations
116+ about that.
117+ '';
118+ };
119+
120+ keystoneAdminUsername = mkOption {
121+ type = types.str;
122+ default = "admin";
123+ description = ''
124+ The keystone admin user name used to create the Glance account.
125+ '';
126+ };
127+
128+ keystoneAdminPassword = mkSecretOption {
129+ name = "keystoneAdminPassword";
130+ description = ''
131+ The keystone admin user's password.
132+ '';
133+ };
134+
135+ keystoneAdminTenant = mkOption {
136+ type = types.str;
137+ default = "admin";
138+ description = ''
139+ The keystone admin tenant used to create the Glance account.
140+ '';
141+ };
142+ keystoneAuthUrl = mkOption {
143+ type = types.str;
144+ default = "http://localhost:5000/v2.0";
145+ description = ''
146+ The keystone auth url used to create the Glance account.
147+ '';
148+ };
149+ };
150+ };
151+
152+ config = mkIf cfg.enable {
153+ users.extraUsers = [{
154+ name = "glance";
155+ group = "glance";
156+ uid = config.ids.gids.glance;
157+
158+ }];
159+ users.extraGroups = [{
160+ name = "glance";
161+ gid = config.ids.gids.glance;
162+ }];
163+
164+ systemd.services.glance-registry = {
165+ description = "OpenStack Glance Registry Daemon";
166+ after = [ "network.target"];
167+ path = [ pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
168+ wantedBy = [ "multi-user.target" ];
169+ preStart = ''
170+ mkdir -m 775 -p /var/lib/glance/{images,scrubber,image_cache}
171+ chown glance:glance /var/lib/glance/{images,scrubber,image_cache}
172+
173+ # Secret file managment
174+ cp ${glanceRegistryConfTpl} ${glanceRegistryConf};
175+ chown glance:glance ${glanceRegistryConf};
176+ chmod 640 ${glanceRegistryConf}
177+ ${replaceSecret cfg.database.password glanceRegistryConf}
178+ ${replaceSecret cfg.servicePassword glanceRegistryConf}
179+
180+ cp ${glanceApiConfTpl} ${glanceApiConf};
181+ chown glance:glance ${glanceApiConf};
182+ chmod 640 ${glanceApiConf}
183+ ${replaceSecret cfg.database.password glanceApiConf}
184+ ${replaceSecret cfg.servicePassword glanceApiConf}
185+
186+ # Initialise the database
187+ ${cfg.package}/bin/glance-manage --config-file=${glanceApiConf} --config-file=${glanceRegistryConf} db_sync
188+ '';
189+ postStart = ''
190+ set -eu
191+ export OS_AUTH_URL=${cfg.bootstrap.keystoneAuthUrl}
192+ export OS_USERNAME=${cfg.bootstrap.keystoneAdminUsername}
193+ export OS_PASSWORD=${getSecret cfg.bootstrap.keystoneAdminPassword}
194+ export OS_TENANT_NAME=${cfg.bootstrap.keystoneAdminTenant}
195+
196+ # Wait until the keystone is available for use
197+ count=0
198+ while ! keystone user-get ${cfg.bootstrap.keystoneAdminUsername} > /dev/null
199+ do
200+ if [ $count -eq 30 ]
201+ then
202+ echo "Tried 30 times, giving up..."
203+ exit 1
204+ fi
205+
206+ echo "Keystone not yet started. Waiting for 1 second..."
207+ count=$((count++))
208+ sleep 1
209+ done
210+
211+ # If the service glance doesn't exist, we consider glance is
212+ # not initialized
213+ if ! keystone service-get glance
214+ then
215+ keystone service-create --type image --name glance
216+ ID=$(keystone service-get glance | awk '/ id / { print $4 }')
217+ keystone endpoint-create --region RegionOne --service $ID --internalurl http://localhost:9292 --adminurl http://localhost:9292 --publicurl ${cfg.bootstrap.endpointPublic}
218+
219+ keystone user-create --name ${cfg.serviceUsername} --tenant service --pass ${getSecret cfg.servicePassword}
220+ keystone user-role-add --tenant service --user ${cfg.serviceUsername} --role admin
221+ fi
222+ '';
223+ serviceConfig = {
224+ PermissionsStartOnly = true; # preStart must be run as root
225+ TimeoutStartSec = "600"; # 10min for initial db migrations
226+ User = "glance";
227+ Group = "glance";
228+ ExecStart = "${cfg.package}/bin/glance-registry --config-file=${glanceRegistryConf}";
229+ };
230+ };
231+ systemd.services.glance-api = {
232+ description = "OpenStack Glance API Daemon";
233+ after = [ "glance-registry.service" "network.target"];
234+ requires = [ "glance-registry.service" "network.target"];
235+ wantedBy = [ "multi-user.target" ];
236+ serviceConfig = {
237+ PermissionsStartOnly = true; # preStart must be run as root
238+ User = "glance";
239+ Group = "glance";
240+ ExecStart = "${cfg.package}/bin/glance-api --config-file=${glanceApiConf}";
241+ };
242+ };
243+ };
244+
245+}