diff -Nru maas-2.1.1+bzr5544/CHANGELOG maas-2.1.3+bzr5573/CHANGELOG --- maas-2.1.1+bzr5544/CHANGELOG 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/CHANGELOG 2016-12-21 07:56:19.000000000 -0500 @@ -2,6 +2,62 @@ Changelog ========= +2.1.3 +===== + +Bugs fixed in this release +-------------------------- + +LP: #1611999 [2.1] MAAS cannot power query with Cisco UCSM power driver. + +LP: #1604962 node set to "failed deployment" for no visible reason + +LP: #1614584 MAAS fails to start when gethostname returns an FQDN. + +LP: #1582323 Select MAAS datasource specifically to ensure commissioning doesn't fail when competing cloud metadata resides on disk + +LP: #1646163 [2.1] Icon need to be improved + +LP: #1646162 [2.1] Sticky header has been removed + +LP: #1646160 [2.1] Device discovery UI does not have a loading state + +LP: #1628126 [2.1, FUJ] Column layout shouldn't resize until screen is smaller than 1440 + +LP: #1628058 [2.1, FUJ] The form spacing is not compatible to the designs + +LP: #1628054 [2.1, FUJ] Section complete/incomplete icon + +LP: #1639182 [2.1] log format differs for Yakkety + +LP: #1637401 Re-adding virsh chassis to discover new nodes powers down existing nodes + +LP: #1642033 IPs with multiple names can corrupt DNS zone data + +LP: #1646955 'main_archive_hostname' is not defined + +LP: #1651452 Use correct escaping for EFI's kernel command line datasource_list. + + +2.1.2 +===== + +Bugs fixed in this release +-------------------------- + +LP: #1516065 Fix IPMI chassis config session timeout issue when configuring the boot device. Only error on PowerAuthErrors when configuring the IPMI chassis boot order. + +LP: #1642996 2.x preseeds with {{escape.shell}} fail if not upgraded at 2.1.1 + +LP: #1643057 juju2 with maas 2.1.1 LXD containers get wrong ip addresses + +LP: #1640300 Be defensive in the postgresql listener when a system notification is received for a none existent handler or missing listener channel. + +LP: #1613862 Re-allow configuration of the port where to connect to postgresql. + +LP: #1638575 Add two capabilities: bridging-interface-ubuntu and bridging-automatic-ubuntu. + + 2.1.1 ===== diff -Nru maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata --- maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata 2016-12-21 07:56:19.000000000 -0500 @@ -24,6 +24,3 @@ driver_06_depmod: ["curtin", "in-target", "--", "depmod"] driver_07_update_initramfs: ["curtin", "in-target", "--", "update-initramfs", "-u"] {{endif}} - -power_state: - mode: reboot diff -Nru maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_centos maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_centos --- maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_centos 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_centos 2016-12-21 07:56:19.000000000 -0500 @@ -7,6 +7,3 @@ late_commands: maas: [wget, '--no-proxy', '{{node_disable_pxe_url}}', '--post-data', '{{node_disable_pxe_data}}', '-O', '/dev/null'] - -power_state: - mode: reboot diff -Nru maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_custom maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_custom --- maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_custom 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_custom 2016-12-21 07:56:19.000000000 -0500 @@ -7,6 +7,3 @@ late_commands: maas: [wget, '--no-proxy', '{{node_disable_pxe_url}}', '--post-data', '{{node_disable_pxe_data}}', '-O', '/dev/null'] - -power_state: - mode: reboot diff -Nru maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_suse maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_suse --- maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_suse 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_suse 2016-12-21 07:56:19.000000000 -0500 @@ -7,6 +7,3 @@ late_commands: maas: [wget, '--no-proxy', '{{node_disable_pxe_url}}', '--post-data', '{{node_disable_pxe_data}}', '-O', '/dev/null'] - -power_state: - mode: reboot diff -Nru maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_windows maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_windows --- maas-2.1.1+bzr5544/contrib/preseeds_v2/curtin_userdata_windows 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/contrib/preseeds_v2/curtin_userdata_windows 2016-12-21 07:56:19.000000000 -0500 @@ -9,6 +9,3 @@ maas: [wget, '--no-proxy', '{{node_disable_pxe_url}}', '--post-data', '{{node_disable_pxe_data}}', '-O', '/dev/null'] license_key: {{node.get_effective_license_key()}} - -power_state: - mode: reboot diff -Nru maas-2.1.1+bzr5544/debian/changelog maas-2.1.3+bzr5573/debian/changelog --- maas-2.1.1+bzr5544/debian/changelog 2016-11-08 15:01:28.000000000 -0500 +++ maas-2.1.3+bzr5573/debian/changelog 2016-12-22 08:25:09.000000000 -0500 @@ -1,3 +1,12 @@ +maas (2.1.3+bzr5573-0ubuntu1~16.10.1) yakkety-proposed; urgency=medium + + * Stable Release Update. New MAAS upstream bugfix release 2.1.3 (LP: #1644071) + - MAAS 2.1.3 is a new upstream bugfix release that fixes several bugs + present on MAAS 2.1.0. This is solely a bugfix release. To see what + issues this release fixes, please refer to the CHANGELOG. + + -- Andres Rodriguez Thu, 22 Dec 2016 08:22:55 -0500 + maas (2.1.1+bzr5544-0ubuntu1~16.10.1) yakkety-proposed; urgency=medium * Stable Release Update. New MAAS upstream bugfix release 2.1.1 (LP: #1639766) diff -Nru maas-2.1.1+bzr5544/debian/maas-rack-controller.maas-rackd.service maas-2.1.3+bzr5573/debian/maas-rack-controller.maas-rackd.service --- maas-2.1.1+bzr5544/debian/maas-rack-controller.maas-rackd.service 2016-11-08 14:56:26.000000000 -0500 +++ maas-2.1.3+bzr5573/debian/maas-rack-controller.maas-rackd.service 2016-12-21 07:56:26.000000000 -0500 @@ -18,10 +18,11 @@ # Logs go to the journal; read them with # journalctl -u maas-rackd # Logs also go to $LOGFILE for backward compatibility. The journal -# should now be preferred as the $LOGFILE will be removed in MAAS 1.9. +# should now be preferred as the $LOGFILE will be removed. ExecStart=/bin/sh -c \ 'exec /usr/bin/authbind --deep /usr/bin/twistd3 --nodaemon --pidfile= \ - maas-rackd 2>&1 | tee -a $LOGFILE' + --logger=provisioningserver.logger.EventLogger maas-rackd 2>&1 | \ + tee -a $LOGFILE' [Install] WantedBy=multi-user.target diff -Nru maas-2.1.1+bzr5544/debian/maas-region-api.maas-regiond-worker@.service maas-2.1.3+bzr5573/debian/maas-region-api.maas-regiond-worker@.service --- maas-2.1.1+bzr5544/debian/maas-region-api.maas-regiond-worker@.service 2016-11-08 14:56:26.000000000 -0500 +++ maas-2.1.3+bzr5573/debian/maas-region-api.maas-regiond-worker@.service 2016-12-21 07:56:26.000000000 -0500 @@ -19,9 +19,11 @@ # Logs go to the journal; read them with: # journalctl -u maas-regiond-worker@* # Logs also go to $LOGFILE for backward compatibility. The journal -# should now be preferred as $LOGFILE will be removed in MAAS 1.9. +# should now be preferred as $LOGFILE will be removed. ExecStart=/bin/sh -c \ - 'exec twistd3 --nodaemon --pidfile= maas-regiond 2>&1 | tee -a $LOGFILE' + 'exec twistd3 --nodaemon --pidfile= \ + --logger=provisioningserver.logger.EventLogger maas-regiond 2>&1 | \ + tee -a $LOGFILE' [Install] WantedBy=maas-regiond.service diff -Nru maas-2.1.1+bzr5544/debian/rules maas-2.1.3+bzr5573/debian/rules --- maas-2.1.1+bzr5544/debian/rules 2016-11-08 14:56:26.000000000 -0500 +++ maas-2.1.3+bzr5573/debian/rules 2016-12-21 07:56:26.000000000 -0500 @@ -81,4 +81,4 @@ | sed -rne 's,^Version: ([^-]+).*,\1,p') get-orig-source: bzr export -r $(REV) --root=maas-$(VER).orig \ - maas_$(VER).orig.tar.gz lp:maas + maas_$(VER).orig.tar.gz lp:maas/2.1 diff -Nru maas-2.1.1+bzr5544/docs/changelog.rst maas-2.1.3+bzr5573/docs/changelog.rst --- maas-2.1.1+bzr5544/docs/changelog.rst 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/docs/changelog.rst 2016-12-21 07:56:19.000000000 -0500 @@ -2,6 +2,62 @@ Changelog ========= +2.1.3 +===== + +Bugs fixed in this release +-------------------------- + +LP: #1611999 [2.1] MAAS cannot power query with Cisco UCSM power driver. + +LP: #1604962 node set to "failed deployment" for no visible reason + +LP: #1614584 MAAS fails to start when gethostname returns an FQDN. + +LP: #1582323 Select MAAS datasource specifically to ensure commissioning doesn't fail when competing cloud metadata resides on disk + +LP: #1646163 [2.1] Icon need to be improved + +LP: #1646162 [2.1] Sticky header has been removed + +LP: #1646160 [2.1] Device discovery UI does not have a loading state + +LP: #1628126 [2.1, FUJ] Column layout shouldn't resize until screen is smaller than 1440 + +LP: #1628058 [2.1, FUJ] The form spacing is not compatible to the designs + +LP: #1628054 [2.1, FUJ] Section complete/incomplete icon + +LP: #1639182 [2.1] log format differs for Yakkety + +LP: #1637401 Re-adding virsh chassis to discover new nodes powers down existing nodes + +LP: #1642033 IPs with multiple names can corrupt DNS zone data + +LP: #1646955 'main_archive_hostname' is not defined + +LP: #1651452 Use correct escaping for EFI's kernel command line datasource_list. + + +2.1.2 +===== + +Bugs fixed in this release +-------------------------- + +LP: #1516065 Fix IPMI chassis config session timeout issue when configuring the boot device. Only error on PowerAuthErrors when configuring the IPMI chassis boot order. + +LP: #1642996 2.x preseeds with {{escape.shell}} fail if not upgraded at 2.1.1 + +LP: #1643057 juju2 with maas 2.1.1 LXD containers get wrong ip addresses + +LP: #1640300 Be defensive in the postgresql listener when a system notification is received for a none existent handler or missing listener channel. + +LP: #1613862 Re-allow configuration of the port where to connect to postgresql. + +LP: #1638575 Add two capabilities: bridging-interface-ubuntu and bridging-automatic-ubuntu. + + 2.1.1 ===== diff -Nru maas-2.1.1+bzr5544/docs/version.rst maas-2.1.3+bzr5573/docs/version.rst --- maas-2.1.1+bzr5544/docs/version.rst 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/docs/version.rst 2016-12-21 07:56:19.000000000 -0500 @@ -67,3 +67,15 @@ Deploy nodes with custom network layout and configuration. Available since version 1.9 on Ubuntu deployments. See :ref:`networking` for more about this feature. + +.. _cap_bridging-interface-ubuntu: + +``bridging-interface-ubuntu`` + Deploy nodes, selectively configuring bridges on network interfaces. + Available since 2.1 on Ubuntu deployments. + +.. _cap_bridging-automatic-ubuntu: + +``bridging-automatic-ubuntu`` + Deploy nodes, automatically configuring bridges on all interfaces. + Available since 2.1 on Ubuntu deployments. diff -Nru maas-2.1.1+bzr5544/MANIFEST.in maas-2.1.3+bzr5573/MANIFEST.in --- maas-2.1.1+bzr5544/MANIFEST.in 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/MANIFEST.in 2016-12-21 07:56:19.000000000 -0500 @@ -1,11 +1,6 @@ -graft src/*/static -graft src/*/templates -graft src/*/fixtures -graft src/*/specs -graft src/provisioningserver/* -graft src/metadataserver/commissioning +graft src/maasserver/static +graft src/maasserver/templates +graft src/metadataserver/fixtures +graft src/provisioningserver/templates include src/maasserver/migrations/south/django16_south_maas19.tar.gz -prune src/*/testing -prune src/*/tests -prune src/maastesting -prune src/provisioningserver/*/tests +include src/provisioningserver/drivers/power/*.xml diff -Nru maas-2.1.1+bzr5544/services/rackd/run maas-2.1.3+bzr5573/services/rackd/run --- maas-2.1.1+bzr5544/services/rackd/run 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/services/rackd/run 2016-12-21 07:56:19.000000000 -0500 @@ -30,4 +30,5 @@ script="$(readlink -f bin/twistd.rack)" exec $(command -v authbind && echo --deep) \ - "${script}" --nodaemon --pidfile="" maas-rackd + "${script}" --logger=provisioningserver.logger.EventLogger \ + --nodaemon --pidfile="" maas-rackd diff -Nru maas-2.1.1+bzr5544/services/regiond/run maas-2.1.3+bzr5573/services/regiond/run --- maas-2.1.1+bzr5544/services/regiond/run 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/services/regiond/run 2016-12-21 07:56:19.000000000 -0500 @@ -33,4 +33,6 @@ # Exec the MAAS API and Web UI Server. script="$(readlink -f bin/twistd.region)" -exec "${script}" --nodaemon --pidfile="" maas-regiond +exec "${script}" \ + --logger=provisioningserver.logger.EventLogger \ + --nodaemon --pidfile="" maas-regiond diff -Nru maas-2.1.1+bzr5544/services/regiond2/run maas-2.1.3+bzr5573/services/regiond2/run --- maas-2.1.1+bzr5544/services/regiond2/run 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/services/regiond2/run 2016-12-21 07:56:19.000000000 -0500 @@ -33,4 +33,6 @@ # Exec the MAAS API and Web UI Server. script="$(readlink -f bin/twistd.region)" -exec "${script}" --nodaemon --pidfile="" maas-regiond +exec "${script}" \ + --logger=provisioningserver.logger.EventLogger \ + --nodaemon --pidfile="" maas-regiond diff -Nru maas-2.1.1+bzr5544/setup.py maas-2.1.3+bzr5573/setup.py --- maas-2.1.1+bzr5544/setup.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/setup.py 2016-12-21 07:56:19.000000000 -0500 @@ -89,6 +89,7 @@ "*.testing", "*.tests", "maastesting", + "maastesting.*", ], ), package_dir={'': 'src'}, diff -Nru maas-2.1.1+bzr5544/src/maas/settings.py maas-2.1.3+bzr5573/src/maas/settings.py --- maas-2.1.1+bzr5544/src/maas/settings.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maas/settings.py 2016-12-21 07:56:19.000000000 -0500 @@ -124,7 +124,7 @@ 'USER': config.database_user, 'PASSWORD': config.database_pass, 'HOST': config.database_host, - 'PORT': '', + 'PORT': str(config.database_port), } } except: diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/bcache_cacheset.py maas-2.1.3+bzr5573/src/maasserver/api/bcache_cacheset.py --- maas-2.1.1+bzr5544/src/maasserver/api/bcache_cacheset.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/bcache_cacheset.py 2016-12-21 07:56:19.000000000 -0500 @@ -25,6 +25,7 @@ DISPLAYED_CACHE_SET_FIELDS = ( + 'system_id', 'id', 'name', 'cache_device', @@ -85,7 +86,7 @@ def resource_uri(cls, cache_set=None): # See the comment in NodeHandler.resource_uri. system_id = "system_id" - cache_set_id = "cache_set_id" + cache_set_id = "id" if cache_set is not None: cache_set_id = cache_set.id node = cache_set.get_node() @@ -94,19 +95,24 @@ return ('bcache_cache_set_handler', (system_id, cache_set_id)) @classmethod + def system_id(cls, cache_set): + node = cache_set.get_node() + return None if node is None else node.system_id + + @classmethod def cache_device(cls, cache_set): """Return the cache device for this cache set.""" return cache_set.get_device() - def read(self, request, system_id, cache_set_id): + def read(self, request, system_id, id): """Read bcache cache set on a machine. Returns 404 if the machine or cache set is not found. """ return CacheSet.objects.get_cache_set_or_404( - system_id, cache_set_id, request.user, NODE_PERMISSION.VIEW) + system_id, id, request.user, NODE_PERMISSION.VIEW) - def delete(self, request, system_id, cache_set_id): + def delete(self, request, system_id, id): """Delete cache set on a machine. Returns 400 if the cache set is in use. @@ -114,7 +120,7 @@ Returns 409 if the machine is not Ready. """ cache_set = CacheSet.objects.get_cache_set_or_404( - system_id, cache_set_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = cache_set.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -126,7 +132,7 @@ cache_set.delete() return rc.DELETED - def update(self, request, system_id, cache_set_id): + def update(self, request, system_id, id): """Delete bcache on a machine. :param cache_device: Cache block device to replace current one. @@ -138,7 +144,7 @@ Returns 409 if the machine is not Ready. """ cache_set = CacheSet.objects.get_cache_set_or_404( - system_id, cache_set_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = cache_set.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/bcache.py maas-2.1.3+bzr5573/src/maasserver/api/bcache.py --- maas-2.1.1+bzr5544/src/maasserver/api/bcache.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/bcache.py 2016-12-21 07:56:19.000000000 -0500 @@ -25,6 +25,7 @@ DISPLAYED_BCACHE_FIELDS = ( + 'system_id', 'id', 'uuid', 'name', @@ -96,7 +97,7 @@ def resource_uri(cls, bcache=None): # See the comment in NodeHandler.resource_uri. system_id = "system_id" - bcache_id = "bcache_id" + bcache_id = "id" if bcache is not None: bcache_id = bcache.id node = bcache.get_node() @@ -105,6 +106,11 @@ return ('bcache_device_handler', (system_id, bcache_id)) @classmethod + def system_id(cls, bcache): + node = bcache.get_node() + return None if node is None else node.system_id + + @classmethod def size(cls, bcache): return bcache.get_size() @@ -122,22 +128,22 @@ """Return the backing device for this bcache.""" return bcache.get_bcache_backing_filesystem().get_parent() - def read(self, request, system_id, bcache_id): + def read(self, request, system_id, id): """Read bcache device on a machine. Returns 404 if the machine or bcache is not found. """ return Bcache.objects.get_object_or_404( - system_id, bcache_id, request.user, NODE_PERMISSION.VIEW) + system_id, id, request.user, NODE_PERMISSION.VIEW) - def delete(self, request, system_id, bcache_id): + def delete(self, request, system_id, id): """Delete bcache on a machine. Returns 404 if the machine or bcache is not found. Returns 409 if the machine is not Ready. """ bcache = Bcache.objects.get_object_or_404( - system_id, bcache_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = bcache.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -145,7 +151,7 @@ bcache.delete() return rc.DELETED - def update(self, request, system_id, bcache_id): + def update(self, request, system_id, id): """Delete bcache on a machine. :param name: Name of the Bcache. @@ -162,7 +168,7 @@ Returns 409 if the machine is not Ready. """ bcache = Bcache.objects.get_object_or_404( - system_id, bcache_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = bcache.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/blockdevices.py maas-2.1.3+bzr5573/src/maasserver/api/blockdevices.py --- maas-2.1.1+bzr5544/src/maasserver/api/blockdevices.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/blockdevices.py 2016-12-21 07:56:19.000000000 -0500 @@ -38,6 +38,7 @@ DISPLAYED_BLOCKDEVICE_FIELDS = ( + 'system_id', 'id', 'name', 'uuid', @@ -125,13 +126,17 @@ # See the comment in NodeHandler.resource_uri. if block_device is None: system_id = "system_id" - device_id = "device_id" + device_id = "id" else: device_id = block_device.id system_id = block_device.node.system_id return ('blockdevice_handler', (system_id, device_id)) @classmethod + def system_id(cls, block_device): + return block_device.node.system_id + + @classmethod def name(cls, block_device): return block_device.actual_instance.get_name() @@ -201,15 +206,15 @@ return [] return partition_table.partitions.all() - def read(self, request, system_id, device_id): + def read(self, request, system_id, id): """Read block device on node. Returns 404 if the machine or block device is not found. """ return BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.VIEW) + system_id, id, request.user, NODE_PERMISSION.VIEW) - def delete(self, request, system_id, device_id): + def delete(self, request, system_id, id): """Delete block device on a machine. Returns 404 if the machine or block device is not found. @@ -217,7 +222,7 @@ Returns 409 if the machine is not Ready. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = device.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -225,7 +230,7 @@ device.delete() return rc.DELETED - def update(self, request, system_id, device_id): + def update(self, request, system_id, id): """Update block device on a machine. Machines must have a status of Ready to have access to all options. @@ -256,7 +261,7 @@ Returns 409 if the machine is not Ready. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = device.get_node() if node.status not in [NODE_STATUS.READY, NODE_STATUS.DEPLOYED]: raise NodeStateViolation( @@ -285,7 +290,7 @@ raise MAASAPIValidationError(form.errors) @operation(idempotent=False) - def add_tag(self, request, system_id, device_id): + def add_tag(self, request, system_id, id): """Add a tag to block device on a machine. :param tag: The tag being added. @@ -295,7 +300,7 @@ Returns 409 if the machine is not Ready. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = device.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -305,7 +310,7 @@ return device @operation(idempotent=False) - def remove_tag(self, request, system_id, device_id): + def remove_tag(self, request, system_id, id): """Remove a tag from block device on a machine. :param tag: The tag being removed. @@ -315,7 +320,7 @@ Returns 409 if the machine is not Ready. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = device.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -325,7 +330,7 @@ return device @operation(idempotent=False) - def format(self, request, system_id, device_id): + def format(self, request, system_id, id): """Format block device with filesystem. :param fstype: Type of filesystem. @@ -337,7 +342,7 @@ Returns 409 if the machine is not Ready or Allocated. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = device.get_node() raise_error_for_invalid_state_on_allocated_operations( node, request.user, "format") @@ -348,7 +353,7 @@ raise MAASAPIValidationError(form.errors) @operation(idempotent=False) - def unformat(self, request, system_id, device_id): + def unformat(self, request, system_id, id): """Unformat block device with filesystem. Returns 400 if the block device is not formatted, currently mounted, \ @@ -359,7 +364,7 @@ Returns 409 if the machine is not Ready or Allocated. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = device.get_node() raise_error_for_invalid_state_on_allocated_operations( node, request.user, "unformat") @@ -381,7 +386,7 @@ return device @operation(idempotent=False) - def mount(self, request, system_id, device_id): + def mount(self, request, system_id, id): """Mount the filesystem on block device. :param mount_point: Path on the filesystem to mount. @@ -393,7 +398,7 @@ Returns 409 if the machine is not Ready or Allocated. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) raise_error_for_invalid_state_on_allocated_operations( device.get_node(), request.user, "mount") filesystem = device.get_effective_filesystem() @@ -405,7 +410,7 @@ raise MAASAPIValidationError(form.errors) @operation(idempotent=False) - def unmount(self, request, system_id, device_id): + def unmount(self, request, system_id, id): """Unmount the filesystem on block device. Returns 400 if the block device is not formatted or not currently \ @@ -416,7 +421,7 @@ Returns 409 if the machine is not Ready or Allocated. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = device.get_node() raise_error_for_invalid_state_on_allocated_operations( node, request.user, "unmount") @@ -431,7 +436,7 @@ return device @operation(idempotent=False) - def set_boot_disk(self, request, system_id, device_id): + def set_boot_disk(self, request, system_id, id): """Set this block device as the boot disk for the machine. Returns 400 if the block device is a virtual block device. @@ -440,7 +445,7 @@ Returns 409 if the machine is not Ready or Allocated. """ device = BlockDevice.objects.get_block_device_or_404( - system_id, device_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = device.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/boot_resources.py maas-2.1.3+bzr5573/src/maasserver/api/boot_resources.py --- maas-2.1.1+bzr5544/src/maasserver/api/boot_resources.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/boot_resources.py 2016-12-21 07:56:19.000000000 -0500 @@ -25,6 +25,7 @@ from maasserver.api.utils import get_optional_param from maasserver.bootresources import ( import_resources, + is_import_resources_running, stop_import_resources, ) from maasserver.enum import ( @@ -236,6 +237,11 @@ "Import of boot resources is being stopped", content_type=("text/plain; charset=%s" % settings.DEFAULT_CHARSET)) + @operation(idempotent=True) + def is_importing(self, request): + """Return import status.""" + return is_import_resources_running() + @classmethod def resource_uri(cls, *args, **kwargs): return ('boot_resources_handler', []) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/boot_source_selections.py maas-2.1.3+bzr5573/src/maasserver/api/boot_source_selections.py --- maas-2.1.1+bzr5544/src/maasserver/api/boot_source_selections.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/boot_source_selections.py 2016-12-21 07:56:19.000000000 -0500 @@ -21,6 +21,7 @@ DISPLAYED_BOOTSOURCESELECTION_FIELDS = ( + 'boot_source_id', 'id', 'os', 'release', diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/dhcpsnippets.py maas-2.1.3+bzr5573/src/maasserver/api/dhcpsnippets.py --- maas-2.1.1+bzr5544/src/maasserver/api/dhcpsnippets.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/dhcpsnippets.py 2016-12-21 07:56:19.000000000 -0500 @@ -48,7 +48,7 @@ if dhcp_snippet is not None: dhcp_snippet_id = dhcp_snippet.id else: - dhcp_snippet_id = "dhcp_snippet_id" + dhcp_snippet_id = "id" return ('dhcp_snippet_handler', (dhcp_snippet_id,)) @classmethod @@ -70,16 +70,15 @@ def global_snippet(handler, dhcp_snippet): return dhcp_snippet.node is None and dhcp_snippet.subnet is None - def read(self, request, dhcp_snippet_id): + def read(self, request, id): """Read DHCP snippet. Returns 404 if the snippet is not found. """ - return DHCPSnippet.objects.get_dhcp_snippet_or_404( - dhcp_snippet_id) + return DHCPSnippet.objects.get_dhcp_snippet_or_404(id) @admin_method - def update(self, request, dhcp_snippet_id): + def update(self, request, id): """Update a DHCP snippet. :param name: The name of the DHCP snippet. @@ -109,8 +108,7 @@ Returns 404 if the DHCP snippet is not found. """ - dhcp_snippet = DHCPSnippet.objects.get_dhcp_snippet_or_404( - dhcp_snippet_id) + dhcp_snippet = DHCPSnippet.objects.get_dhcp_snippet_or_404(id) form = DHCPSnippetForm(instance=dhcp_snippet, data=request.data) if form.is_valid(): return form.save() @@ -118,19 +116,18 @@ raise MAASAPIValidationError(form.errors) @admin_method - def delete(self, request, dhcp_snippet_id): + def delete(self, request, id): """Delete a DHCP snippet. Returns 404 if the DHCP snippet is not found. """ - dhcp_snippet = DHCPSnippet.objects.get_dhcp_snippet_or_404( - dhcp_snippet_id) + dhcp_snippet = DHCPSnippet.objects.get_dhcp_snippet_or_404(id) dhcp_snippet.delete() return rc.DELETED @admin_method @operation(idempotent=False) - def revert(self, request, dhcp_snippet_id): + def revert(self, request, id): """Revert the value of a DHCP snippet to an earlier revision. :param to: What revision in the DHCP snippet's history to revert to. @@ -149,8 +146,7 @@ raise MAASAPIValidationError( "%s is an invalid 'to' value" % revert_to) - dhcp_snippet = DHCPSnippet.objects.get_dhcp_snippet_or_404( - dhcp_snippet_id) + dhcp_snippet = DHCPSnippet.objects.get_dhcp_snippet_or_404(id) try: def gc_hook(value): dhcp_snippet.value = value diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/dnsresourcerecords.py maas-2.1.3+bzr5573/src/maasserver/api/dnsresourcerecords.py --- maas-2.1.1+bzr5544/src/maasserver/api/dnsresourcerecords.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/dnsresourcerecords.py 2016-12-21 07:56:19.000000000 -0500 @@ -153,7 +153,7 @@ @classmethod def resource_uri(cls, dnsresourcerecord=None): # See the comment in NodeHandler.resource_uri. - dnsresourcerecord_id = "dnsresourcerecord_id" + dnsresourcerecord_id = "id" if dnsresourcerecord is not None: dnsresourcerecord_id = dnsresourcerecord.id return ('dnsresourcerecord_handler', (dnsresourcerecord_id,)) @@ -163,15 +163,15 @@ """Return the name of the dnsresourcerecord.""" return dnsresourcerecord.fqdn() - def read(self, request, dnsresourcerecord_id): + def read(self, request, id): """Read dnsresourcerecord. Returns 404 if the dnsresourcerecord is not found. """ return DNSData.objects.get_dnsdata_or_404( - dnsresourcerecord_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, dnsresourcerecord_id): + def update(self, request, id): """Update dnsresourcerecord. :param rrtype: Resource Type @@ -182,7 +182,7 @@ Returns 404 if the dnsresourcerecord is not found. """ dnsdata = DNSData.objects.get_dnsdata_or_404( - dnsresourcerecord_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) request.data['dnsresource'] = dnsdata.dnsresource.id form = DNSDataForm(instance=dnsdata, data=request.data) if form.is_valid(): @@ -190,7 +190,7 @@ else: raise MAASAPIValidationError(form.errors) - def delete(self, request, dnsresourcerecord_id): + def delete(self, request, id): """Delete dnsresourcerecord. Returns 403 if the user does not have permission to delete the @@ -198,7 +198,7 @@ Returns 404 if the dnsresourcerecord is not found. """ dnsdata = DNSData.objects.get_dnsdata_or_404( - dnsresourcerecord_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) dnsrr = dnsdata.dnsresource dnsdata.delete() if dnsrr.dnsdata_set.count() == 0 and dnsrr.ip_addresses.count() == 0: diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/dnsresources.py maas-2.1.3+bzr5573/src/maasserver/api/dnsresources.py --- maas-2.1.1+bzr5544/src/maasserver/api/dnsresources.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/dnsresources.py 2016-12-21 07:56:19.000000000 -0500 @@ -124,7 +124,7 @@ @classmethod def resource_uri(cls, dnsresource=None): # See the comment in NodeHandler.resource_uri. - dnsresource_id = "dnsresource_id" + dnsresource_id = "id" if dnsresource is not None: dnsresource_id = dnsresource.id return ('dnsresource_handler', (dnsresource_id,)) @@ -144,15 +144,15 @@ """Other data for this dnsresource.""" return dnsresource.dnsdata_set.all().order_by('rrtype') - def read(self, request, dnsresource_id): + def read(self, request, id): """Read dnsresource. Returns 404 if the dnsresource is not found. """ return DNSResource.objects.get_dnsresource_or_404( - dnsresource_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, dnsresource_id): + def update(self, request, id): """Update dnsresource. :param fqdn: Hostname (with domain) for the dnsresource. @@ -163,14 +163,14 @@ Returns 404 if the dnsresource is not found. """ dnsresource = DNSResource.objects.get_dnsresource_or_404( - dnsresource_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) form = DNSResourceForm(instance=dnsresource, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors) - def delete(self, request, dnsresource_id): + def delete(self, request, id): """Delete dnsresource. Returns 403 if the user does not have permission to delete the @@ -178,6 +178,6 @@ Returns 404 if the dnsresource is not found. """ dnsresource = DNSResource.objects.get_dnsresource_or_404( - dnsresource_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) dnsresource.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/domains.py maas-2.1.3+bzr5573/src/maasserver/api/domains.py --- maas-2.1.1+bzr5544/src/maasserver/api/domains.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/domains.py 2016-12-21 07:56:19.000000000 -0500 @@ -96,7 +96,7 @@ @classmethod def resource_uri(cls, domain=None): # See the comment in NodeHandler.resource_uri. - domain_id = "domain_id" + domain_id = "id" if domain is not None: domain_id = domain.id return ('domain_handler', (domain_id,)) @@ -111,15 +111,15 @@ """Return DNSResources within the specified domain.""" return domain.dnsresource_set.all() - def read(self, request, domain_id): + def read(self, request, id): """Read domain. Returns 404 if the domain is not found. """ return Domain.objects.get_domain_or_404( - domain_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, domain_id): + def update(self, request, id): """Update domain. :param name: Name of the domain. @@ -131,14 +131,14 @@ Returns 404 if the domain is not found. """ domain = Domain.objects.get_domain_or_404( - domain_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) form = DomainForm(instance=domain, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors) - def delete(self, request, domain_id): + def delete(self, request, id): """Delete domain. Returns 403 if the user does not have permission to update the @@ -146,6 +146,6 @@ Returns 404 if the domain is not found. """ domain = Domain.objects.get_domain_or_404( - domain_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) domain.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/fabrics.py maas-2.1.3+bzr5573/src/maasserver/api/fabrics.py --- maas-2.1.1+bzr5544/src/maasserver/api/fabrics.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/fabrics.py 2016-12-21 07:56:19.000000000 -0500 @@ -62,7 +62,7 @@ @classmethod def resource_uri(cls, fabric=None): # See the comment in NodeHandler.resource_uri. - fabric_id = "fabric_id" + fabric_id = "id" if fabric is not None: fabric_id = fabric.id return ('fabric_handler', (fabric_id,)) @@ -77,15 +77,15 @@ """Return VLANs within the specified fabric.""" return fabric.vlan_set.all() - def read(self, request, fabric_id): + def read(self, request, id): """Read fabric. Returns 404 if the fabric is not found. """ return Fabric.objects.get_fabric_or_404( - fabric_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, fabric_id): + def update(self, request, id): """Update fabric. :param name: Name of the fabric. @@ -95,19 +95,19 @@ Returns 404 if the fabric is not found. """ fabric = Fabric.objects.get_fabric_or_404( - fabric_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) form = FabricForm(instance=fabric, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors) - def delete(self, request, fabric_id): + def delete(self, request, id): """Delete fabric. Returns 404 if the fabric is not found. """ fabric = Fabric.objects.get_fabric_or_404( - fabric_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) fabric.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/fannetworks.py maas-2.1.3+bzr5573/src/maasserver/api/fannetworks.py --- maas-2.1.1+bzr5544/src/maasserver/api/fannetworks.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/fannetworks.py 2016-12-21 07:56:19.000000000 -0500 @@ -70,20 +70,20 @@ @classmethod def resource_uri(cls, fannetwork=None): # See the comment in NodeHandler.resource_uri. - fannetwork_id = "fannetwork_id" + fannetwork_id = "id" if fannetwork is not None: fannetwork_id = fannetwork.id return ('fannetwork_handler', (fannetwork_id,)) - def read(self, request, fannetwork_id): + def read(self, request, id): """Read fannetwork. Returns 404 if the fannetwork is not found. """ return FanNetwork.objects.get_fannetwork_or_404( - fannetwork_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, fannetwork_id): + def update(self, request, id): """Update fannetwork. :param name: Name of the fannetwork. @@ -97,19 +97,19 @@ Returns 404 if the fannetwork is not found. """ fannetwork = FanNetwork.objects.get_fannetwork_or_404( - fannetwork_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) form = FanNetworkForm(instance=fannetwork, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors) - def delete(self, request, fannetwork_id): + def delete(self, request, id): """Delete fannetwork. Returns 404 if the fannetwork is not found. """ fannetwork = FanNetwork.objects.get_fannetwork_or_404( - fannetwork_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) fannetwork.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/interfaces.py maas-2.1.3+bzr5573/src/maasserver/api/interfaces.py --- maas-2.1.1+bzr5544/src/maasserver/api/interfaces.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/interfaces.py 2016-12-21 07:56:19.000000000 -0500 @@ -55,6 +55,7 @@ BLANK_FIELD = "This field cannot be blank." DISPLAYED_INTERFACE_FIELDS = ( + 'system_id', 'id', 'name', 'type', @@ -340,7 +341,7 @@ def resource_uri(cls, interface=None): # See the comment in NodeHandler.resource_uri. system_id = "system_id" - interface_id = "interface_id" + interface_id = "id" if interface is not None: interface_id = interface.id node = interface.get_node() @@ -349,6 +350,11 @@ return ('interface_handler', (system_id, interface_id)) @classmethod + def system_id(cls, interface): + node = interface.get_node() + return None if node is None else node.system_id + + @classmethod def mac_address(cls, interface): if interface.mac_address is not None: return "%s" % interface.mac_address @@ -381,15 +387,15 @@ def effective_mtu(cls, interface): return interface.get_effective_mtu() - def read(self, request, system_id, interface_id): + def read(self, request, system_id, id): """Read interface on node. Returns 404 if the node or interface is not found. """ return Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.VIEW) + system_id, id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, system_id, interface_id): + def update(self, request, system_id, id): """Update interface on node. Machines must has status of Ready or Broken to have access to all @@ -490,7 +496,7 @@ Returns 404 if the node or interface is not found. """ interface = Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = interface.get_node() if node.node_type == NODE_TYPE.MACHINE: # This node needs to be in the correct state to modify @@ -524,13 +530,13 @@ form.errors['parent'] = form.errors.pop('parents') raise MAASAPIValidationError(form.errors) - def delete(self, request, system_id, interface_id): + def delete(self, request, system_id, id): """Delete interface on node. Returns 404 if the node or interface is not found. """ interface = Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = interface.get_node() raise_error_if_controller(node, "delete interface") if node.node_type == NODE_TYPE.MACHINE: @@ -542,7 +548,7 @@ return rc.DELETED @operation(idempotent=False) - def link_subnet(self, request, system_id, interface_id): + def link_subnet(self, request, system_id, id): """Link interface to a subnet. :param mode: AUTO, DHCP, STATIC or LINK_UP connection to subnet. @@ -573,7 +579,7 @@ Returns 404 if the node or interface is not found. """ interface = Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = interface.get_node() raise_error_if_controller(node, "link subnet") if node.node_type == NODE_TYPE.MACHINE: @@ -598,7 +604,7 @@ raise MAASAPIValidationError(form.errors) @operation(idempotent=False) - def unlink_subnet(self, request, system_id, interface_id): + def unlink_subnet(self, request, system_id, id): """Unlink interface to a subnet. :param id: ID of the link on the interface to remove. @@ -606,7 +612,7 @@ Returns 404 if the node or interface is not found. """ interface = Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = interface.get_node() raise_error_if_controller(node, "link subnet") if node.node_type == NODE_TYPE.MACHINE: @@ -621,7 +627,7 @@ raise MAASAPIValidationError(form.errors) @operation(idempotent=False) - def set_default_gateway(self, request, system_id, interface_id): + def set_default_gateway(self, request, system_id, id): """Set the node to use this interface as the default gateway. If this interface has more than one subnet with a gateway IP in the @@ -635,7 +641,7 @@ Returns 404 if the node or interface is not found. """ interface = Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) node = interface.get_node() raise_error_if_controller(node, "link subnet") if node.node_type == NODE_TYPE.MACHINE: @@ -651,7 +657,7 @@ raise MAASAPIValidationError(form.errors) @operation(idempotent=False) - def add_tag(self, request, system_id, interface_id): + def add_tag(self, request, system_id, id): """Add a tag to interface on a node. :param tag: The tag being added. @@ -660,13 +666,13 @@ Returns 403 if the user is not allowed to update the interface. """ interface = Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) interface.add_tag(get_mandatory_param(request.POST, 'tag')) interface.save() return interface @operation(idempotent=False) - def remove_tag(self, request, system_id, interface_id): + def remove_tag(self, request, system_id, id): """Remove a tag from interface on a node. :param tag: The tag being removed. @@ -675,7 +681,7 @@ Returns 403 if the user is not allowed to update the interface. """ interface = Interface.objects.get_interface_or_404( - system_id, interface_id, request.user, NODE_PERMISSION.EDIT) + system_id, id, request.user, NODE_PERMISSION.EDIT) interface.remove_tag(get_mandatory_param(request.POST, 'tag')) interface.save() return interface diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/ipranges.py maas-2.1.3+bzr5573/src/maasserver/api/ipranges.py --- maas-2.1.1+bzr5544/src/maasserver/api/ipranges.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/ipranges.py 2016-12-21 07:56:19.000000000 -0500 @@ -81,20 +81,20 @@ @classmethod def resource_uri(cls, iprange=None): # See the comment in NodeHandler.resource_uri. - iprange_id = "iprange_id" + iprange_id = "id" if iprange is not None: iprange_id = iprange.id return ('iprange_handler', (iprange_id,)) - def read(self, request, iprange_id): + def read(self, request, id): """Read IP range. Returns 404 if the IP range is not found. """ - iprange = IPRange.objects.get_iprange_or_404(iprange_id) + iprange = IPRange.objects.get_iprange_or_404(id) return iprange - def update(self, request, iprange_id): + def update(self, request, id): """Update IP range. :param start_ip: Start IP address of this range (inclusive). @@ -104,7 +104,7 @@ Returns 403 if not owner of IP range. Returns 404 if the IP Range is not found. """ - iprange = IPRange.objects.get_iprange_or_404(iprange_id) + iprange = IPRange.objects.get_iprange_or_404(id) raise_error_if_not_owner(iprange, request.user) form = IPRangeForm(instance=iprange, data=request.data) if form.is_valid(): @@ -112,13 +112,13 @@ else: raise MAASAPIValidationError(form.errors) - def delete(self, request, iprange_id): + def delete(self, request, id): """Delete IP range. Returns 403 if not owner of IP range. Returns 404 if the IP range is not found. """ - iprange = IPRange.objects.get_iprange_or_404(iprange_id) + iprange = IPRange.objects.get_iprange_or_404(id) raise_error_if_not_owner(iprange, request.user) iprange.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/machines.py maas-2.1.3+bzr5573/src/maasserver/api/machines.py --- maas-2.1.1+bzr5544/src/maasserver/api/machines.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/machines.py 2016-12-21 07:56:19.000000000 -0500 @@ -811,8 +811,9 @@ fields = DISPLAYED_ANON_MACHINE_FIELDS @classmethod - def resource_uri(cls, machine): - return ('machine_handler', (machine.system_id, )) + def resource_uri(cls, machine=None): + system_id = "system_id" if machine is None else machine.system_id + return ('machine_handler', (system_id, )) class AnonMachinesHandler(AnonNodesHandler): diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/nodes.py maas-2.1.3+bzr5573/src/maasserver/api/nodes.py --- maas-2.1.1+bzr5544/src/maasserver/api/nodes.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/nodes.py 2016-12-21 07:56:19.000000000 -0500 @@ -188,11 +188,27 @@ @classmethod def resource_uri(cls, node=None): - # This method is called by piston in two different contexts: - # - when generating an uri template to be used in the documentation - # (in this case, it is called with node=None). - # - when populating the 'resource_uri' field of an object - # returned by the API (in this case, node is a Node object). + # + # This method is called by Piston in two different contexts: + # + # 1. When generating a URI template to be used in the documentation, + # in which case it is called with `node=None`. We return argument + # *names* instead of their values. Frustratingly, Piston itself + # discards these names and instead uses names derived from Django's + # URL patterns for the resource. + # + # 2. When populating the `resource_uri` field of an object returned by + # the API, in which case `node` is an instance of `Node`. + # + # There is a check made at handler class creation time to ensure that + # the names from #1 match up to the handler's `fields`. In this way we + # can declare which fields are required to render a resource's URI and + # be sure that they are all present in a rendering of said resource. + # + # There is an additional unit test (see `TestResourceURIs`) to check + # that the fields in each URI template match up to those fields + # declared in a handler's `resource_uri` method. + # node_system_id = "system_id" if node is not None: node_system_id = node.system_id @@ -247,6 +263,8 @@ read = create = update = delete = None model = Node + resource_uri = NodeHandler.resource_uri + class AnonNodesHandler(AnonymousOperationsHandler): """Anonymous access to Nodes.""" diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/packagerepositories.py maas-2.1.3+bzr5573/src/maasserver/api/packagerepositories.py --- maas-2.1.1+bzr5544/src/maasserver/api/packagerepositories.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/packagerepositories.py 2016-12-21 07:56:19.000000000 -0500 @@ -45,19 +45,18 @@ if package_repository is not None: package_repository_id = package_repository.id else: - package_repository_id = "package_repository_id" + package_repository_id = "id" return ('package_repository_handler', (package_repository_id,)) - def read(self, request, package_repository_id): + def read(self, request, id): """Read Package Repository. Returns 404 if the repository is not found. """ - return PackageRepository.objects.get_object_or_404( - package_repository_id) + return PackageRepository.objects.get_object_or_404(id) @admin_method - def update(self, request, package_repository_id): + def update(self, request, id): """Update a Package Repository. :param name: The name of the Package Repository. @@ -83,8 +82,7 @@ Returns 404 if the Package Repository is not found. """ - package_repository = PackageRepository.objects.get_object_or_404( - package_repository_id) + package_repository = PackageRepository.objects.get_object_or_404(id) form = PackageRepositoryForm( instance=package_repository, data=request.data) if form.is_valid(): @@ -93,13 +91,12 @@ raise MAASAPIValidationError(form.errors) @admin_method - def delete(self, request, package_repository_id): + def delete(self, request, id): """Delete a Package Repository. Returns 404 if the Package Repository is not found. """ - package_repository = PackageRepository.objects.get_object_or_404( - package_repository_id) + package_repository = PackageRepository.objects.get_object_or_404(id) package_repository.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/partitions.py maas-2.1.3+bzr5573/src/maasserver/api/partitions.py --- maas-2.1.1+bzr5544/src/maasserver/api/partitions.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/partitions.py 2016-12-21 07:56:19.000000000 -0500 @@ -33,6 +33,8 @@ DISPLAYED_PARTITION_FIELDS = ( + 'system_id', + 'device_id', 'id', 'uuid', 'path', @@ -145,7 +147,7 @@ if partition is None: system_id = "system_id" device_id = "device_id" - partition_id = "partition_id" + partition_id = "id" else: partition_id = partition.id block_device = partition.partition_table.block_device @@ -154,7 +156,16 @@ return ( 'partition_handler', (system_id, device_id, partition_id)) - def read(self, request, system_id, device_id, partition_id): + @classmethod + def system_id(cls, partition): + block_device = partition.partition_table.block_device + return block_device.node.system_id + + @classmethod + def device_id(cls, partition): + return partition.partition_table.block_device.id + + def read(self, request, system_id, device_id, id): """Read partition. Returns 404 if the node, block device, or partition are not found. @@ -164,9 +175,9 @@ partition_table = get_object_or_404( PartitionTable, block_device=device) return get_partition_by_id_or_name__or_404( - partition_id, partition_table) + id, partition_table) - def delete(self, request, system_id, device_id, partition_id): + def delete(self, request, system_id, device_id, id): """Delete partition. Returns 404 if the node, block device, or partition are not found. @@ -176,7 +187,7 @@ partition_table = get_object_or_404( PartitionTable, block_device=device) partition = get_partition_by_id_or_name__or_404( - partition_id, partition_table) + id, partition_table) node = device.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -185,7 +196,7 @@ return rc.DELETED @operation(idempotent=False) - def format(self, request, system_id, device_id, partition_id): + def format(self, request, system_id, device_id, id): """Format a partition. :param fstype: Type of filesystem. @@ -201,7 +212,7 @@ partition_table = get_object_or_404( PartitionTable, block_device=device) partition = get_partition_by_id_or_name__or_404( - partition_id, partition_table) + id, partition_table) node = device.get_node() raise_error_for_invalid_state_on_allocated_operations( node, request.user, "format") @@ -212,14 +223,14 @@ return form.save() @operation(idempotent=False) - def unformat(self, request, system_id, device_id, partition_id): + def unformat(self, request, system_id, device_id, id): """Unformat a partition.""" device = BlockDevice.objects.get_block_device_or_404( system_id, device_id, request.user, NODE_PERMISSION.EDIT) partition_table = get_object_or_404( PartitionTable, block_device=device) partition = get_partition_by_id_or_name__or_404( - partition_id, partition_table) + id, partition_table) node = device.get_node() raise_error_for_invalid_state_on_allocated_operations( node, request.user, "unformat") @@ -240,7 +251,7 @@ return partition @operation(idempotent=False) - def mount(self, request, system_id, device_id, partition_id): + def mount(self, request, system_id, device_id, id): """Mount the filesystem on partition. :param mount_point: Path on the filesystem to mount. @@ -255,7 +266,7 @@ partition_table = get_object_or_404( PartitionTable, block_device=device) partition = get_partition_by_id_or_name__or_404( - partition_id, partition_table) + id, partition_table) raise_error_for_invalid_state_on_allocated_operations( device.get_node(), request.user, "mount") filesystem = partition.get_effective_filesystem() @@ -267,7 +278,7 @@ raise MAASAPIValidationError(form.errors) @operation(idempotent=False) - def unmount(self, request, system_id, device_id, partition_id): + def unmount(self, request, system_id, device_id, id): """Unmount the filesystem on partition. Returns 400 if the partition is not formatted or not currently \ @@ -281,7 +292,7 @@ partition_table = get_object_or_404( PartitionTable, block_device=device) partition = get_partition_by_id_or_name__or_404( - partition_id, partition_table) + id, partition_table) node = device.get_node() raise_error_for_invalid_state_on_allocated_operations( node, request.user, "unmount") diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/raid.py maas-2.1.3+bzr5573/src/maasserver/api/raid.py --- maas-2.1.1+bzr5544/src/maasserver/api/raid.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/raid.py 2016-12-21 07:56:19.000000000 -0500 @@ -26,6 +26,7 @@ DISPLAYED_RAID_FIELDS = ( + 'system_id', 'id', 'uuid', 'name', @@ -95,7 +96,7 @@ def resource_uri(cls, raid=None): # See the comment in NodeHandler.resource_uri. system_id = "system_id" - raid_id = "raid_id" + raid_id = "id" if raid is not None: raid_id = raid.id node = raid.get_node() @@ -104,6 +105,11 @@ return ('raid_device_handler', (system_id, raid_id)) @classmethod + def system_id(cls, raid): + node = raid.get_node() + return None if node is None else node.system_id + + @classmethod def level(cls, raid): """Return the level of RAID for device.""" # group_type holds the raid level, we just expose it over the API @@ -141,15 +147,15 @@ fstype=FILESYSTEM_TYPE.RAID_SPARE) ] - def read(self, request, system_id, raid_id): + def read(self, request, system_id, id): """Read RAID device on a machine. Returns 404 if the machine or RAID is not found. """ return RAID.objects.get_object_or_404( - system_id, raid_id, request.user, NODE_PERMISSION.VIEW) + system_id, id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, system_id, raid_id): + def update(self, request, system_id, id): """Update RAID on a machine. :param name: Name of the RAID. @@ -169,7 +175,7 @@ Returns 409 if the machine is not Ready. """ raid = RAID.objects.get_object_or_404( - system_id, raid_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = raid.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -180,14 +186,14 @@ else: raise MAASAPIValidationError(form.errors) - def delete(self, request, system_id, raid_id): + def delete(self, request, system_id, id): """Delete RAID on a machine. Returns 404 if the machine or RAID is not found. Returns 409 if the machine is not Ready. """ raid = RAID.objects.get_object_or_404( - system_id, raid_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = raid.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/spaces.py maas-2.1.3+bzr5573/src/maasserver/api/spaces.py --- maas-2.1.1+bzr5544/src/maasserver/api/spaces.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/spaces.py 2016-12-21 07:56:19.000000000 -0500 @@ -60,7 +60,7 @@ @classmethod def resource_uri(cls, space=None): # See the comment in NodeHandler.resource_uri. - space_id = "space_id" + space_id = "id" if space is not None: space_id = space.id return ('space_handler', (space_id,)) @@ -75,15 +75,15 @@ """Return all subnets in this space.""" return space.subnet_set.all() - def read(self, request, space_id): + def read(self, request, id): """Read space. Returns 404 if the space is not found. """ return Space.objects.get_space_or_404( - space_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, space_id): + def update(self, request, id): """Update space. :param name: Name of the space. @@ -92,19 +92,19 @@ Returns 404 if the space is not found. """ space = Space.objects.get_space_or_404( - space_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) form = SpaceForm(instance=space, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors) - def delete(self, request, space_id): + def delete(self, request, id): """Delete space. Returns 404 if the space is not found. """ space = Space.objects.get_space_or_404( - space_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) space.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/ssh_keys.py maas-2.1.3+bzr5573/src/maasserver/api/ssh_keys.py --- maas-2.1.1+bzr5544/src/maasserver/api/ssh_keys.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/ssh_keys.py 2016-12-21 07:56:19.000000000 -0500 @@ -108,21 +108,21 @@ model = SSHKey create = update = None - def read(self, request, keyid): + def read(self, request, id): """GET an SSH key. Returns 404 if the key does not exist. """ - key = get_object_or_404(SSHKey, id=keyid) + key = get_object_or_404(SSHKey, id=id) return key - def delete(self, request, keyid): + def delete(self, request, id): """DELETE an SSH key. Returns 404 if the key does not exist. Returns 401 if the key does not belong to the calling user. """ - key = get_object_or_404(SSHKey, id=keyid) + key = get_object_or_404(SSHKey, id=id) if key.user != request.user: return HttpResponseForbidden( "Can't delete a key you don't own.", @@ -141,7 +141,7 @@ @classmethod def resource_uri(cls, sshkey=None): - keyid = "keyid" + keyid = "id" if sshkey is not None: keyid = sshkey.id return ('sshkey_handler', (keyid, )) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/ssl_keys.py maas-2.1.3+bzr5573/src/maasserver/api/ssl_keys.py --- maas-2.1.1+bzr5544/src/maasserver/api/ssl_keys.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/ssl_keys.py 2016-12-21 07:56:19.000000000 -0500 @@ -72,25 +72,25 @@ model = SSLKey create = update = None - def read(self, request, keyid): + def read(self, request, id): """GET an SSL key. - Returns 404 if the keyid is not found. + Returns 404 if the key with `id` is not found. Returns 401 if the key does not belong to the requesting user. """ - key = get_object_or_404(SSLKey, id=keyid) + key = get_object_or_404(SSLKey, id=id) if key.user != request.user: return HttpResponseForbidden( "Can't get a key you don't own.") return key - def delete(self, request, keyid): + def delete(self, request, id): """DELETE an SSL key. Returns 401 if the key does not belong to the requesting user. Returns 204 if the key is successfully deleted. """ - key = get_object_or_404(SSLKey, id=keyid) + key = get_object_or_404(SSLKey, id=id) if key.user != request.user: return HttpResponseForbidden( "Can't delete a key you don't own.", @@ -102,7 +102,7 @@ @classmethod def resource_uri(cls, sslkey=None): - keyid = "keyid" + keyid = "id" if sslkey is not None: keyid = sslkey.id return ('sslkey_handler', (keyid, )) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/staticroutes.py maas-2.1.3+bzr5573/src/maasserver/api/staticroutes.py --- maas-2.1.1+bzr5544/src/maasserver/api/staticroutes.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/staticroutes.py 2016-12-21 07:56:19.000000000 -0500 @@ -64,20 +64,20 @@ @classmethod def resource_uri(cls, staticroute=None): # See the comment in NodeHandler.resource_uri. - staticroute_id = "staticroute_id" + staticroute_id = "id" if staticroute is not None: staticroute_id = staticroute.id return ('staticroute_handler', (staticroute_id,)) - def read(self, request, staticroute_id): + def read(self, request, id): """Read static route. Returns 404 if the static route is not found. """ return StaticRoute.objects.get_staticroute_or_404( - staticroute_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, staticroute_id): + def update(self, request, id): """Update static route. :param source: Source subnet for the route. @@ -88,19 +88,19 @@ Returns 404 if the static route is not found. """ staticroute = StaticRoute.objects.get_staticroute_or_404( - staticroute_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) form = StaticRouteForm(instance=staticroute, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors) - def delete(self, request, staticroute_id): + def delete(self, request, id): """Delete static route. Returns 404 if the static route is not found. """ staticroute = StaticRoute.objects.get_staticroute_or_404( - staticroute_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) staticroute.delete() return rc.DELETED diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/subnets.py maas-2.1.3+bzr5573/src/maasserver/api/subnets.py --- maas-2.1.1+bzr5544/src/maasserver/api/subnets.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/subnets.py 2016-12-21 07:56:19.000000000 -0500 @@ -93,7 +93,7 @@ @classmethod def resource_uri(cls, subnet=None): # See the comment in NodeHandler.resource_uri. - subnet_id = "subnet_id" + subnet_id = "id" if subnet is not None: subnet_id = subnet.id return ('subnet_handler', (subnet_id,)) @@ -108,15 +108,15 @@ """ return subnet.space.get_name() - def read(self, request, subnet_id): + def read(self, request, id): """Read subnet. Returns 404 if the subnet is not found. """ return Subnet.objects.get_subnet_or_404( - subnet_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, subnet_id): + def update(self, request, id): """Update subnet. :param name: Name of the subnet. @@ -134,46 +134,46 @@ Returns 404 if the subnet is not found. """ subnet = Subnet.objects.get_subnet_or_404( - subnet_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) form = SubnetForm(instance=subnet, data=request.data) if form.is_valid(): return form.save() else: raise MAASAPIValidationError(form.errors) - def delete(self, request, subnet_id): + def delete(self, request, id): """Delete subnet. Returns 404 if the subnet is not found. """ subnet = Subnet.objects.get_subnet_or_404( - subnet_id, request.user, NODE_PERMISSION.ADMIN) + id, request.user, NODE_PERMISSION.ADMIN) subnet.delete() return rc.DELETED @operation(idempotent=True) - def reserved_ip_ranges(self, request, subnet_id): + def reserved_ip_ranges(self, request, id): """Lists IP ranges currently reserved in the subnet. Returns 404 if the subnet is not found. """ subnet = Subnet.objects.get_subnet_or_404( - subnet_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) return subnet.get_ipranges_in_use().render_json() @operation(idempotent=True) - def unreserved_ip_ranges(self, request, subnet_id): + def unreserved_ip_ranges(self, request, id): """Lists IP ranges currently unreserved in the subnet. Returns 404 if the subnet is not found. """ subnet = Subnet.objects.get_subnet_or_404( - subnet_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) return subnet.get_ipranges_not_in_use().render_json( include_purpose=False) @operation(idempotent=True) - def statistics(self, request, subnet_id): + def statistics(self, request, id): """ Returns statistics for the specified subnet, including: @@ -194,7 +194,7 @@ Returns 404 if the subnet is not found. """ subnet = Subnet.objects.get_subnet_or_404( - subnet_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) include_ranges = get_optional_param( request.GET, 'include_ranges', default=False, validator=StringBool) include_suggestions = get_optional_param( @@ -207,7 +207,7 @@ include_suggestions=include_suggestions) @operation(idempotent=True) - def ip_addresses(self, request, subnet_id): + def ip_addresses(self, request, id): """ Returns a summary of IP addresses assigned to this subnet. @@ -218,7 +218,7 @@ of any node associated with each address. """ subnet = Subnet.objects.get_subnet_or_404( - subnet_id, request.user, NODE_PERMISSION.VIEW) + id, request.user, NODE_PERMISSION.VIEW) with_username = get_optional_param( request.GET, 'with_username', default=True, validator=StringBool) with_node_summary = get_optional_param( diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/support.py maas-2.1.3+bzr5573/src/maasserver/api/support.py --- maas-2.1.1+bzr5544/src/maasserver/api/support.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/support.py 2016-12-21 07:56:19.000000000 -0500 @@ -28,6 +28,10 @@ HttpStatusCode, rc, ) +from provisioningserver.logger import LegacyLogger + + +log = LegacyLogger() class OperationsResource(Resource): @@ -225,9 +229,42 @@ cls.allowed_methods = frozenset( http_method for http_method, name in exports) + # Flags used later. + has_fields = cls.fields is not BaseHandler.fields + has_resource_uri = hasattr(cls, "resource_uri") + is_internal_only = cls.__module__ in {__name__, "metadataserver.api"} + + # Reject handlers which omit fields required for self-referential + # URIs. See bug 1643552. We ignore handlers that don't define `fields` + # because we assume they are doing custom object rendering and we have + # no way to check here for compliance. + if has_fields and has_resource_uri: + _, uri_params, *_ = cls.resource_uri() + missing = set(uri_params).difference(cls.fields) + if len(missing) != 0: + raise OperationsHandlerMisconfigured( + "{handler.__module__}.{handler.__name__} does not render " + "all fields required to construct a self-referential URI. " + "Fields missing: {missing}.".format( + handler=cls, missing=" ".join(sorted(missing)))) + + # Piston uses `resource_uri` even for handlers without models in order + # to generate documentation. We ignore those modules we consider "for + # internal use only" since we do not intend to generate documentation + # for these. + if not has_resource_uri and not is_internal_only: + log.warn( + "{handler.__module__}.{handler.__name__} does not have " + "`resource_uri`. This means it may be omitted from generated " + "documentation. Please investigate.", handler=cls) + return cls +class OperationsHandlerMisconfigured(Exception): + """Handler has been misconfigured; see the error message for details.""" + + class OperationsHandlerMixin: """Handler mixin for operations dispatch. diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_api.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_api.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_api.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_api.py 2016-12-21 07:56:19.000000000 -0500 @@ -6,16 +6,20 @@ __all__ = [] import http.client +from itertools import chain import json import random +import string from unittest.mock import Mock from django.conf import settings from django.core.urlresolvers import reverse +from maasserver import urls_api as urlconf from maasserver.api import ( machines as machines_module, nodes as nodes_module, ) +from maasserver.api.doc import find_api_resources from maasserver.api.nodes import store_node_power_parameters from maasserver.enum import KEYS_PROTOCOL_TYPE from maasserver.exceptions import ( @@ -43,6 +47,8 @@ from maasserver.utils.keys import ImportSSHKeysError from maasserver.utils.orm import get_one from maastesting.matchers import MockCalledOnceWith +from maastesting.testcase import MAASTestCase +from piston3.doc import generate_doc from requests.exceptions import RequestException from testtools.matchers import ( Contains, @@ -51,6 +57,56 @@ ) +class TestResourceURIs(MAASTestCase): + """Tests for `resource_uri` usage in handlers.""" + + def test_resource_uri_in_docs_matches_handlers_idea_of_resource_uri(self): + # Sigh. Piston asks handlers for resource_uri information, but also + # makes use of Django's URL patterns to figure out resource_uri + # templates for the documentation. Here we check that they match up. + formatter = string.Formatter() + + def gen_handlers(resource): + if resource.anonymous is not None: + yield resource.anonymous + if resource.handler is not None: + yield resource.handler + + handlers = chain.from_iterable( + map(gen_handlers, find_api_resources(urlconf))) + + mismatches = [] + + for handler in map(type, handlers): + if hasattr(handler, "resource_uri"): + resource_uri_params = handler.resource_uri()[1] + resource_uri_template = ( + generate_doc(handler).resource_uri_template) + + fields_expected = tuple(resource_uri_params) + fields_observed = tuple( + fname for _, fname, _, _ in formatter.parse( + resource_uri_template) if fname is not None) + + if fields_observed != fields_expected: + mismatches.append( + (handler, fields_expected, fields_observed)) + + if len(mismatches) != 0: + messages = ( + "{handler.__module__}.{handler.__name__} has mismatched " + "fields:\n expected: {expected}\n observed: {observed}" + "".format( + handler=handler, expected=" ".join(expected), + observed=" ".join(observed)) + for handler, expected, observed in mismatches) + messages = chain(messages, [ + "Amend the URL patterns for these handlers/resources so that " + "the observed fields match what is expected.", + ]) + self.fail("\n--\n".join(messages)) + + class TestAuthentication(MAASServerTestCase): """Tests for `maasserver.api.auth`.""" diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_bcache_cacheset.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_bcache_cacheset.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_bcache_cacheset.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_bcache_cacheset.py 2016-12-21 07:56:19.000000000 -0500 @@ -149,6 +149,7 @@ "cache_device": ContainsDict({ "id": Equals(cache_block_device.id), }), + "system_id": Equals(cache_set.get_node().system_id), })) def test_read_404_when_invalid_id(self): diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_bcache.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_bcache.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_bcache.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_bcache.py 2016-12-21 07:56:19.000000000 -0500 @@ -228,6 +228,7 @@ "backing_device": ContainsDict({ "id": Equals(backing_block_device.id), }), + "system_id": Equals(bcache.get_node().system_id), })) def test_read_404_when_not_bcache(self): diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_blockdevice.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_blockdevice.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_blockdevice.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_blockdevice.py 2016-12-21 07:56:19.000000000 -0500 @@ -18,6 +18,7 @@ from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE from maasserver.testing.api import APITestCase from maasserver.testing.factory import factory +from maasserver.testing.matchers import HasStatusCode from maasserver.utils.converters import json_load_bytes from maasserver.utils.orm import reload_object from testtools.matchers import ( @@ -383,6 +384,17 @@ "mount_options": filesystem2.mount_options, }, parsed_device['partitions'][1]['filesystem']) + def test_read_returns_system_id(self): + node = factory.make_Node() + block_device = factory.make_PhysicalBlockDevice(node=node) + uri = get_blockdevice_uri(block_device) + response = self.client.get(uri) + self.assertThat(response, HasStatusCode(http.client.OK)) + parsed_device = json_load_bytes(response.content) + self.assertThat(parsed_device, ContainsDict({ + "system_id": Equals(node.system_id), + })) + def test_delete_returns_403_when_not_admin(self): block_device = factory.make_PhysicalBlockDevice() uri = get_blockdevice_uri(block_device) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_boot_resources.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_boot_resources.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_boot_resources.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_boot_resources.py 2016-12-21 07:56:19.000000000 -0500 @@ -404,6 +404,16 @@ self.assertEqual(http.client.OK, response.status_code) self.assertThat(mock_stop, MockCalledOnceWith()) + def test_is_importing_returns_import_status(self): + mock_running = self.patch( + boot_resources, "is_import_resources_running") + mock_running.return_value = factory.pick_bool() + response = self.client.get( + reverse('boot_resources_handler'), {'op': 'is_importing'}) + self.assertEqual(http.client.OK, response.status_code) + self.assertEqual( + mock_running.return_value, json_load_bytes(response.content)) + class TestBootResourceAPI(APITestCase.ForUser): diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_dhcpsnippets.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_dhcpsnippets.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_dhcpsnippets.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_dhcpsnippets.py 2016-12-21 07:56:19.000000000 -0500 @@ -19,6 +19,7 @@ from maasserver.testing.api import APITestCase from maasserver.testing.factory import factory from maasserver.utils.orm import reload_object +from testtools.matchers import Equals class TestDHCPSnippetAPI(APITestCase.ForUser): @@ -44,22 +45,31 @@ self.assertEqual( http.client.OK, response.status_code, response.content) parsed_dhcp_snippet = json.loads(response.content.decode()) - self.assertItemsEqual({ + self.assertThat(parsed_dhcp_snippet, Equals({ 'id': dhcp_snippet.id, 'name': dhcp_snippet.name, - 'value': dhcp_snippet.value, + 'value': dhcp_snippet.value.data, 'description': dhcp_snippet.description, - 'history': [{ - 'id': dhcp_snippet.value.previous_version.id, - 'value': dhcp_snippet.value.previous_version.data, - 'created': format_datetime(dhcp_snippet.value.created), - }], + 'history': [ + { + 'id': dhcp_snippet.value.id, + 'value': dhcp_snippet.value.data, + 'created': format_datetime( + dhcp_snippet.value.created), + }, + { + 'id': dhcp_snippet.value.previous_version.id, + 'value': dhcp_snippet.value.previous_version.data, + 'created': format_datetime( + dhcp_snippet.value.previous_version.created), + }, + ], 'enabled': dhcp_snippet.enabled, 'node': None, 'subnet': None, 'global_snippet': True, 'resource_uri': self.get_dhcp_snippet_uri(dhcp_snippet), - }, parsed_dhcp_snippet) + })) def test_read_by_name(self): dhcp_snippet = factory.make_DHCPSnippet() @@ -71,22 +81,31 @@ self.assertEqual( http.client.OK, response.status_code, response.content) parsed_dhcp_snippet = json.loads(response.content.decode()) - self.assertItemsEqual({ + self.assertThat(parsed_dhcp_snippet, Equals({ 'id': dhcp_snippet.id, 'name': dhcp_snippet.name, - 'value': dhcp_snippet.value, + 'value': dhcp_snippet.value.data, 'description': dhcp_snippet.description, - 'history': [{ - 'id': dhcp_snippet.value.previous_version.id, - 'value': dhcp_snippet.value.previous_version.data, - 'created': format_datetime(dhcp_snippet.value.created), - }], + 'history': [ + { + 'id': dhcp_snippet.value.id, + 'value': dhcp_snippet.value.data, + 'created': format_datetime( + dhcp_snippet.value.created), + }, + { + 'id': dhcp_snippet.value.previous_version.id, + 'value': dhcp_snippet.value.previous_version.data, + 'created': format_datetime( + dhcp_snippet.value.previous_version.created), + }, + ], 'enabled': dhcp_snippet.enabled, 'node': None, 'subnet': None, 'global_snippet': True, 'resource_uri': self.get_dhcp_snippet_uri(dhcp_snippet), - }, parsed_dhcp_snippet) + })) def test_read_404_when_bad_id(self): response = self.client.get( diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_interfaces.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_interfaces.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_interfaces.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_interfaces.py 2016-12-21 07:56:19.000000000 -0500 @@ -831,6 +831,7 @@ "resource_uri": Equals(get_interface_uri(bond)), "params": Equals(bond.params), "effective_mtu": Equals(bond.get_effective_mtu()), + "system_id": Equals(node.system_id), })) self.assertEqual(sorted( nic.name diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_partitions.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_partitions.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_partitions.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_partitions.py 2016-12-21 07:56:19.000000000 -0500 @@ -165,9 +165,13 @@ parsed_partition = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) - self.assertTrue(parsed_partition['bootable']) - self.assertEqual(partition.id, parsed_partition['id']) - self.assertEqual(partition.size, parsed_partition['size']) + self.assertThat(parsed_partition, ContainsDict({ + "bootable": Is(True), + "id": Equals(partition.id), + "size": Equals(partition.size), + "system_id": Equals(device.node.system_id), + "device_id": Equals(device.id), + })) def test_read_partition_by_name(self): device = factory.make_PhysicalBlockDevice( diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_raid.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_raid.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_raid.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_raid.py 2016-12-21 07:56:19.000000000 -0500 @@ -818,6 +818,7 @@ "human_size": Equals( human_readable_bytes(raid.get_size())), "resource_uri": Equals(get_raid_device_uri(raid)), + "system_id": Equals(node.system_id), })) self.assertItemsEqual( block_device_ids + partitions_ids, parsed_device_ids) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_user.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_user.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_user.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_user.py 2016-12-21 07:56:19.000000000 -0500 @@ -27,6 +27,11 @@ ) +def get_user_uri(user): + """Return a User URI.""" + return reverse('user_handler', args=[user.username]) + + class TestUsers(APITestCase.ForUser): def test_handler_path(self): @@ -169,6 +174,7 @@ self.assertEqual(user.username, returned_user['username']) self.assertEqual(user.email, returned_user['email']) self.assertFalse(returned_user['is_superuser']) + self.assertEqual(get_user_uri(user), returned_user['resource_uri']) def test_GET_shows_expected_fields(self): user = factory.make_User() @@ -181,7 +187,7 @@ returned_user = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertItemsEqual( - ['username', 'email', 'is_superuser'], + ['username', 'email', 'resource_uri', 'is_superuser'], returned_user.keys()) def test_GET_identifies_superuser_as_such(self): diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_vlans.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_vlans.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_vlans.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_vlans.py 2016-12-21 07:56:19.000000000 -0500 @@ -132,6 +132,7 @@ "name": Equals(vlan.get_name()), "vid": Equals(vlan.vid), "fabric": Equals(fabric.get_name()), + "fabric_id": Equals(fabric.id), "resource_uri": Equals(get_vlan_uri(vlan)), })) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/tests/test_volume_groups.py maas-2.1.3+bzr5573/src/maasserver/api/tests/test_volume_groups.py --- maas-2.1.1+bzr5544/src/maasserver/api/tests/test_volume_groups.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/tests/test_volume_groups.py 2016-12-21 07:56:19.000000000 -0500 @@ -249,6 +249,7 @@ "human_used_size": Equals( human_readable_bytes(volume_group.get_lvm_allocated_size())), "resource_uri": Equals(get_volume_group_uri(volume_group)), + "system_id": Equals(node.system_id), })) self.assertItemsEqual( block_device_ids + partitions_ids, parsed_device_ids) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/users.py maas-2.1.3+bzr5573/src/maasserver/api/users.py --- maas-2.1.1+bzr5544/src/maasserver/api/users.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/users.py 2016-12-21 07:56:19.000000000 -0500 @@ -112,3 +112,8 @@ user.delete() return rc.DELETED + + @classmethod + def resource_uri(cls, user=None): + username = "username" if user is None else user.username + return ('user_handler', [username]) diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/version.py maas-2.1.3+bzr5573/src/maasserver/api/version.py --- maas-2.1.1+bzr5544/src/maasserver/api/version.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/version.py 2016-12-21 07:56:19.000000000 -0500 @@ -21,6 +21,8 @@ CAP_DEVICES_MANAGEMENT = 'devices-management' CAP_STORAGE_DEPLOYMENT_UBUNTU = 'storage-deployment-ubuntu' CAP_NETWORK_DEPLOYMENT_UBUNTU = 'network-deployment-ubuntu' +CAP_BRIDGING_INTERFACE_UBUNTU = 'bridging-interface-ubuntu' +CAP_BRIDGING_AUTOMATIC_UBUNTU = 'bridging-automatic-ubuntu' API_CAPABILITIES_LIST = [ CAP_NETWORKS_MANAGEMENT, @@ -29,6 +31,8 @@ CAP_DEVICES_MANAGEMENT, CAP_STORAGE_DEPLOYMENT_UBUNTU, CAP_NETWORK_DEPLOYMENT_UBUNTU, + CAP_BRIDGING_INTERFACE_UBUNTU, + CAP_BRIDGING_AUTOMATIC_UBUNTU, ] diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/vlans.py maas-2.1.3+bzr5573/src/maasserver/api/vlans.py --- maas-2.1.1+bzr5544/src/maasserver/api/vlans.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/vlans.py 2016-12-21 07:56:19.000000000 -0500 @@ -21,6 +21,7 @@ 'name', 'vid', 'fabric', + 'fabric_id', 'mtu', 'primary_rack', 'secondary_rack', diff -Nru maas-2.1.1+bzr5544/src/maasserver/api/volume_groups.py maas-2.1.3+bzr5573/src/maasserver/api/volume_groups.py --- maas-2.1.1+bzr5544/src/maasserver/api/volume_groups.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/api/volume_groups.py 2016-12-21 07:56:19.000000000 -0500 @@ -31,6 +31,7 @@ DISPLAYED_VOLUME_GROUP_FIELDS = ( + 'system_id', 'id', 'uuid', 'name', @@ -99,7 +100,7 @@ def resource_uri(cls, volume_group=None): # See the comment in NodeHandler.resource_uri. system_id = "system_id" - volume_group_id = "volume_group_id" + volume_group_id = "id" if volume_group is not None: volume_group_id = volume_group.id node = volume_group.get_node() @@ -108,6 +109,14 @@ return ('volume_group_handler', (system_id, volume_group_id)) @classmethod + def system_id(cls, volume_group): + if volume_group is None: + return None + else: + node = volume_group.get_node() + return None if node is None else node.system_id + + @classmethod def size(cls, filesystem_group): return filesystem_group.get_size() @@ -142,15 +151,15 @@ for filesystem in volume_group.filesystems.all() ] - def read(self, request, system_id, volume_group_id): + def read(self, request, system_id, id): """Read volume group on a machine. Returns 404 if the machine or volume group is not found. """ return VolumeGroup.objects.get_object_or_404( - system_id, volume_group_id, request.user, NODE_PERMISSION.VIEW) + system_id, id, request.user, NODE_PERMISSION.VIEW) - def update(self, request, system_id, volume_group_id): + def update(self, request, system_id, id): """Read volume group on a machine. :param name: Name of the volume group. @@ -165,7 +174,7 @@ Returns 409 if the machine is not Ready. """ volume_group = VolumeGroup.objects.get_object_or_404( - system_id, volume_group_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = volume_group.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -176,14 +185,14 @@ else: return form.save() - def delete(self, request, system_id, volume_group_id): + def delete(self, request, system_id, id): """Delete volume group on a machine. Returns 404 if the machine or volume group is not found. Returns 409 if the machine is not Ready. """ volume_group = VolumeGroup.objects.get_object_or_404( - system_id, volume_group_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = volume_group.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -192,7 +201,7 @@ return rc.DELETED @operation(idempotent=False) - def create_logical_volume(self, request, system_id, volume_group_id): + def create_logical_volume(self, request, system_id, id): """Create a logical volume in the volume group. :param name: Name of the logical volume. @@ -203,7 +212,7 @@ Returns 409 if the machine is not Ready. """ volume_group = VolumeGroup.objects.get_object_or_404( - system_id, volume_group_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = volume_group.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( @@ -216,7 +225,7 @@ return form.save() @operation(idempotent=False) - def delete_logical_volume(self, request, system_id, volume_group_id): + def delete_logical_volume(self, request, system_id, id): """Delete a logical volume in the volume group. :param id: ID of the logical volume. @@ -226,7 +235,7 @@ Returns 409 if the machine is not Ready. """ volume_group = VolumeGroup.objects.get_object_or_404( - system_id, volume_group_id, request.user, NODE_PERMISSION.ADMIN) + system_id, id, request.user, NODE_PERMISSION.ADMIN) node = volume_group.get_node() if node.status != NODE_STATUS.READY: raise NodeStateViolation( diff -Nru maas-2.1.1+bzr5544/src/maasserver/compose_preseed.py maas-2.1.3+bzr5573/src/maasserver/compose_preseed.py --- maas-2.1.1+bzr5544/src/maasserver/compose_preseed.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/compose_preseed.py 2016-12-21 07:56:19.000000000 -0500 @@ -254,12 +254,13 @@ """Compose the preseed value for a node being installed with curtin.""" metadata_url = absolute_reverse('curtin-metadata', base_url=base_url) return _compose_cloud_init_preseed( - node, token, metadata_url, base_url=base_url) + node, token, metadata_url, base_url=base_url, reboot=True) def _compose_cloud_init_preseed( node, token, metadata_url, base_url, poweroff=False, - poweroff_timeout=3600, poweroff_condition=None): + poweroff_timeout=3600, poweroff_condition=None, + reboot=False, reboot_timeout=1800): cloud_config = { 'datasource': { 'MAAS': { @@ -303,6 +304,12 @@ } if poweroff_condition is not None: cloud_config['power_state']['condition'] = poweroff_condition + if reboot: + cloud_config['power_state'] = { + 'delay': 'now', + 'mode': 'reboot', + 'timeout': reboot_timeout, + } return "#cloud-config\n%s" % yaml.safe_dump(cloud_config) diff -Nru maas-2.1.1+bzr5544/src/maasserver/config.py maas-2.1.3+bzr5573/src/maasserver/config.py --- maas-2.1.1+bzr5544/src/maasserver/config.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/config.py 2016-12-21 07:56:19.000000000 -0500 @@ -1,4 +1,4 @@ -# Copyright 2015 Canonical Ltd. This software is licensed under the +# Copyright 2015-2016 Canonical Ltd. This software is licensed under the # GNU Affero General Public License version 3 (see the file LICENSE). """Configuration for the MAAS region.""" @@ -9,6 +9,7 @@ from os import path +from formencode.validators import Int from provisioningserver.config import ( Configuration, ConfigurationFile, @@ -41,6 +42,9 @@ database_host = ConfigurationOption( "database_host", "The address of the PostgreSQL database.", UnicodeString(if_missing="localhost", accept_python=False)) + database_port = ConfigurationOption( + "database_port", "The port of the PostgreSQL database.", + Int(if_missing=5432, accept_python=False, min=1, max=65535)) database_name = ConfigurationOption( "database_name", "The name of the PostgreSQL database.", UnicodeString(if_missing="maasdb", accept_python=False)) diff -Nru maas-2.1.1+bzr5544/src/maasserver/forms_interface_link.py maas-2.1.3+bzr5573/src/maasserver/forms_interface_link.py --- maas-2.1.1+bzr5544/src/maasserver/forms_interface_link.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/forms_interface_link.py 2016-12-21 07:56:19.000000000 -0500 @@ -75,7 +75,7 @@ 'mode', mode_choices), }) if self.instance.vlan is None: - self.fields['subnet'].queryset = Subnet.objects.none() + self.fields['subnet'].queryset = Subnet.objects.all() else: self.fields['subnet'].queryset = ( self.instance.vlan.subnet_set.all()) diff -Nru maas-2.1.1+bzr5544/src/maasserver/listener.py maas-2.1.3+bzr5573/src/maasserver/listener.py --- maas-2.1.1+bzr5544/src/maasserver/listener.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/listener.py 2016-12-21 07:56:19.000000000 -0500 @@ -160,8 +160,20 @@ if self.isSystemChannel(notify.channel): # System level message; pass it to the registered # handler immediately. - handler = self.listeners[notify.channel][0] - handler(notify.channel, notify.payload) + if notify.channel in self.listeners: + # Be defensive in that if a handler does not exist + # for this channel then the channel should be + # unregisted and removed from listeners. + if len(self.listeners[notify.channel]) > 0: + handler = self.listeners[notify.channel][0] + handler(notify.channel, notify.payload) + else: + self.unregisterChannel(notify.channel) + del self.listeners[notify.channel] + else: + # Unregister the channel since no listener is + # registered for this channel. + self.unregisterChannel(notify.channel) else: # Place non-system messages into the queue to be # processed. diff -Nru maas-2.1.1+bzr5544/src/maasserver/management/commands/_config.py maas-2.1.3+bzr5573/src/maasserver/management/commands/_config.py --- maas-2.1.1+bzr5544/src/maasserver/management/commands/_config.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/management/commands/_config.py 2016-12-21 07:56:19.000000000 -0500 @@ -14,7 +14,11 @@ from optparse import make_option import sys -from django.core.management.base import BaseCommand +from django.core.management.base import ( + BaseCommand, + CommandError, +) +import formencode from maascli.utils import parse_docstring from maasserver.config import RegionConfiguration from provisioningserver.config import ConfigurationOption @@ -208,4 +212,9 @@ for name, option in gen_configuration_options(): value = options.get(name) if value is not None: - setattr(config, name, value) + try: + setattr(config, name, value) + except formencode.Invalid as error: + message = str(error).rstrip(".") + raise CommandError("%s: %s." % ( + name.replace("_", "-"), message)) diff -Nru maas-2.1.1+bzr5544/src/maasserver/management/commands/tests/test_config.py maas-2.1.3+bzr5573/src/maasserver/management/commands/tests/test_config.py --- maas-2.1.1+bzr5544/src/maasserver/management/commands/tests/test_config.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/management/commands/tests/test_config.py 2016-12-21 07:56:19.000000000 -0500 @@ -9,6 +9,7 @@ import json from django.core.management import call_command +from django.core.management.base import CommandError from maasserver.config import RegionConfiguration from maasserver.management.commands import _config as config from maasserver.testing.config import RegionConfigurationFixture @@ -76,7 +77,7 @@ self.assertThat(settings, Not(Contains(self.option.dest))) with RegionConfiguration.open() as configuration: self.assertThat(settings, Contains( - getattr(configuration, self.option.dest))) + str(getattr(configuration, self.option.dest)))) class TestConfigurationReset(MAASTestCase): @@ -88,9 +89,12 @@ def test__options_are_reset(self): self.useFixture(RegionConfigurationFixture()) - # Give the option a random value. - value = factory.make_name("foobar") with RegionConfiguration.open_for_update() as configuration: + # Give the option a random value. + if isinstance(getattr(configuration, self.option.dest), str): + value = factory.make_name("foobar") + else: + value = factory.pick_port() setattr(configuration, self.option.dest, value) stdio = call_reset(**{self.option.dest: True}) # Nothing is echoed back to the user. @@ -112,18 +116,53 @@ def test__options_are_saved(self): self.useFixture(RegionConfigurationFixture()) # Set the option to a random value. - value = factory.make_name("foobar") - stdio = call_set(**{self.option.dest: value}) + if self.option.dest == "database_port": + value = factory.pick_port() + else: + value = factory.make_name("foobar") + + # Values are coming from the command-line so stringify. + stdio = call_set(**{self.option.dest: str(value)}) + # Nothing is echoed back to the user. self.assertThat(stdio.getOutput(), Equals("")) self.assertThat(stdio.getError(), Equals("")) + # Some validators alter the given option, like adding an HTTP scheme # to a "bare" URL, so we merely check that the value contains the - # given value, not that it exactly matches. + # given value, not that it exactly matches. Values are converted to a + # str so Contains works with int values. with RegionConfiguration.open() as configuration: self.assertThat( - getattr(configuration, self.option.dest), - Contains(value)) + str(getattr(configuration, self.option.dest)), + Contains(str(value))) + + +class TestConfigurationSet_DatabasePort(MAASTestCase): + """Tests for setting the database port. + + Setting the port is slightly special because the other options are mostly + (at the time of writing) defined using `UnicodeString` validators, roughly + meaning that anything goes, but the port is defined with `Int`. + """ + + def test__exception_when_port_is_not_an_integer(self): + self.useFixture(RegionConfigurationFixture()) + error = self.assertRaises(CommandError, call_set, database_port="foo") + self.assertThat(str(error), Equals( + "database-port: Please enter an integer value.")) + + def test__exception_when_port_is_too_low(self): + self.useFixture(RegionConfigurationFixture()) + error = self.assertRaises(CommandError, call_set, database_port=0) + self.assertThat(str(error), Equals( + "database-port: Please enter a number that is 1 or greater.")) + + def test__exception_when_port_is_too_high(self): + self.useFixture(RegionConfigurationFixture()) + error = self.assertRaises(CommandError, call_set, database_port=2**16) + self.assertThat(str(error), Equals( + "database-port: Please enter a number that is 65535 or smaller.")) class TestConfigurationCommon(MAASTestCase): Binary files /tmp/dE5JCK7kHL/maas-2.1.1+bzr5544/src/maasserver/migrations/south/django16_south_maas19.tar.gz and /tmp/N7yR2fldl6/maas-2.1.3+bzr5573/src/maasserver/migrations/south/django16_south_maas19.tar.gz differ diff -Nru maas-2.1.1+bzr5544/src/maasserver/models/interface.py maas-2.1.3+bzr5573/src/maasserver/models/interface.py --- maas-2.1.1+bzr5544/src/maasserver/models/interface.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/models/interface.py 2016-12-21 07:56:19.000000000 -0500 @@ -858,18 +858,25 @@ :param user: When alloc_type is set to USER_RESERVED, this user will be set on the link. """ + # Allow the interface VLAN to be implied by the subnet VLAN, if we're + # setting up the interface for the first time and it doesn't have + # a VLAN assigned yet. + if self.vlan is None and subnet is not None: + self.vlan = subnet.vlan + self.save(update_fields=['vlan', 'updated']) if mode == INTERFACE_LINK_TYPE.AUTO: - return self._link_subnet_auto(subnet) + result = self._link_subnet_auto(subnet) elif mode == INTERFACE_LINK_TYPE.DHCP: - return self._link_subnet_dhcp(subnet) + result = self._link_subnet_dhcp(subnet) elif mode == INTERFACE_LINK_TYPE.STATIC: - return self._link_subnet_static( + result = self._link_subnet_static( subnet, ip_address=ip_address, alloc_type=alloc_type, user=user) elif mode == INTERFACE_LINK_TYPE.LINK_UP: - return self._link_subnet_link_up(subnet) + result = self._link_subnet_link_up(subnet) else: raise ValueError("Unknown mode: %s" % mode) + return result def force_auto_or_dhcp_link(self): """Force the interface to come up with an AUTO linked to a subnet on diff -Nru maas-2.1.1+bzr5544/src/maasserver/models/node.py maas-2.1.3+bzr5573/src/maasserver/models/node.py --- maas-2.1.1+bzr5544/src/maasserver/models/node.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/models/node.py 2016-12-21 07:56:19.000000000 -0500 @@ -755,7 +755,19 @@ regiond has run on it. Create a region controller only; it can be upgraded to a region+rack controller later if necessary. """ - return self.create(owner=get_worker_user(), hostname=gethostname()) + hostname = gethostname() + # Bug#1614584: it is possible that gethostname() reurns the FQDN. + # Split it up, and get the appropriate domain. If we wind up creating + # one for it, we are not authoritative. + # Just in case the default domain has not been created, let's create it + # here, even if we subsequently overwrite it inside the if statement. + domain = Domain.objects.get_default_domain() + if hostname.find('.') > 0: + hostname, domainname = hostname.split('.', 1) + (domain, _) = Domain.objects.get_or_create( + name=domainname, defaults={'authoritative': False}) + return self.create( + owner=get_worker_user(), hostname=hostname, domain=domain) def get_default_domain(): diff -Nru maas-2.1.1+bzr5544/src/maasserver/models/staticipaddress.py maas-2.1.3+bzr5573/src/maasserver/models/staticipaddress.py --- maas-2.1.1+bzr5544/src/maasserver/models/staticipaddress.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/models/staticipaddress.py 2016-12-21 07:56:19.000000000 -0500 @@ -28,7 +28,6 @@ IntegerField, Manager, PROTECT, - Q, ) from maasserver import ( DefaultMeta, @@ -222,56 +221,152 @@ requested_address, alloc_type, user=user, subnet=subnet) - def _get_user_reserved_mappings(self, domain_or_subnet, raw_ttl=False): - # A poorly named routine these days, since it actually returns - # addresses for anything with any DNSResource records as well. - default_ttl = Config.objects.get_config('default_dns_ttl') - qs = self.filter( - Q(alloc_type=IPADDRESS_TYPE.USER_RESERVED) | - Q(dnsresource__isnull=False)) - # If this is a subnet, we need to ignore subnet.id, as per - # get_hostname_ip_mapping(). LP#1600259 - if isinstance(domain_or_subnet, Subnet): - pass - elif isinstance(domain_or_subnet, Domain): - qs = qs.filter(dnsresource__domain_id=domain_or_subnet.id) - qs = qs.prefetch_related("dnsresource_set") - mappings = defaultdict(HostnameIPMapping) - for instance in qs: - ip = instance.ip - rrset = instance.dnsresource_set.all() - # 2016-01-20 LaMontJones N.B.: - # Empirically, for dnsrr in instance.dnsresource_set.all(): ... - # else: with a non-empty rrset yields both the for loop AND the - # else clause. Wrapping it all in a if/else: avoids that issue. - if rrset.count() > 0: - for dnsrr in rrset: - if dnsrr.name is None or dnsrr.name == '': - hostname = get_ip_based_hostname(ip) - hostname = "%s.%s" % ( - get_ip_based_hostname(ip), - Domain.objects.get_default_domain().name) - else: - hostname = '%s.%s' % (dnsrr.name, dnsrr.domain.name) - if raw_ttl or dnsrr.address_ttl is not None: - ttl = dnsrr.address_ttl - elif dnsrr.domain.ttl is not None: - ttl = dnsrr.domain.ttl - else: - ttl = default_ttl - mappings[hostname].ttl = ttl - mappings[hostname].ips.add(ip) - else: - # No DNSResource, but it's USER_RESERVED. - domain = Domain.objects.get_default_domain() - hostname = "%s.%s" % (get_ip_based_hostname(ip), domain.name) - if raw_ttl or domain.ttl is not None: - ttl = domain.ttl - else: - ttl = default_ttl - mappings[hostname].ttl = ttl - mappings[hostname].ips.add(ip) - return mappings + def _get_special_mappings(self, domain, raw_ttl=False): + """Get the special mappings, possibly limited to a single Domain. + + This function is responsible for creating these mappings: + - any USER_RESERVED IP, + - any IP not associated with a Node, + - any IP associated with a DNSResource. + The caller is responsible for addresses otherwise derived from nodes. + + Because of how the get hostname_ip_mapping code works, we actually need + to fetch ALL of the entries for subnets, but forward mappings need to + be domain-specific. + + :param domain: limit return to just the given Domain. If anything + other than a Domain is passed in (e.g., a Subnet or None), we + return all of the reverse mappings. + :param raw_ttl: Boolean, if True then just return the address_ttl, + otherwise, coalesce the address_ttl to be the correct answer for + zone generation. + :return: a (default) dict of hostname: HostnameIPMapping entries. + """ + default_ttl = "%d" % Config.objects.get_config('default_dns_ttl') + if isinstance(domain, Domain): + # Domains are special in that we only want to have entries for the + # domain that we were asked about. And they can possibly come from + # either the child or the parent for glue. + where_clause = """ + AND ( + dnsrr.dom2_id = %s OR + node.dom2_id = %s OR + dnsrr.domain_id = %s OR + node.domain_id = %s + """ + query_parms = [domain.id, domain.id, domain.id, domain.id] + # And the default domain is extra special, since it needs to have + # A/AAAA RRs for any USER_RESERVED addresses that have no name + # otherwise attached to them. + if domain.is_default(): + where_clause += """ OR ( + dnsrr.fqdn IS NULL AND + node.fqdn IS NULL) + """ + where_clause += ")" + else: + # There is nothing special about the query for subnets. + domain = None + where_clause = "" + query_parms = [] + # raw_ttl says that we don't coalesce, but we need to pick one, so we + # go with DNSResource if it is involved. + if raw_ttl: + ttl_clause = """COALESCE(dnsrr.address_ttl, node.address_ttl)""" + else: + ttl_clause = """ + COALESCE( + dnsrr.address_ttl, + dnsrr.ttl, + node.address_ttl, + node.ttl, + %s)""" % default_ttl + # And here is the SQL query of doom. Build up inner selects to get the + # view of a DNSResource (and Node) that we need, and finally use + # domain2 to handle the case where an FQDN is also the name of a domain + # that we know. + sql_query = """ + SELECT + COALESCE(dnsrr.fqdn, node.fqdn) AS fqdn, + node.system_id, + node.node_type, + """ + ttl_clause + """ AS ttl, + staticip.ip + FROM + maasserver_staticipaddress AS staticip + LEFT JOIN ( + /* Create a dnsrr that has what we need. */ + SELECT + CASE WHEN dnsrr.name = '@' THEN + dom.name + ELSE + CONCAT(dnsrr.name, '.', dom.name) + END AS fqdn, + dom.name as dom_name, + dnsrr.domain_id, + dnsrr.address_ttl, + dom.ttl, + dia.staticipaddress_id AS dnsrr_sip_id, + dom2.id AS dom2_id + FROM maasserver_dnsresource_ip_addresses AS dia + JOIN maasserver_dnsresource AS dnsrr ON + dia.dnsresource_id = dnsrr.id + JOIN maasserver_domain AS dom ON + dnsrr.domain_id = dom.id + LEFT JOIN maasserver_domain AS dom2 ON + CONCAT(dnsrr.name, '.', dom.name) = dom2.name OR ( + dnsrr.name = '@' AND + dom.name SIMILAR TO CONCAT('[-A-Za-z0-9]*.', dom2.name) + ) + ) AS dnsrr ON + dnsrr_sip_id = staticip.id + LEFT JOIN ( + /* Create a node that has what we need. */ + SELECT + CONCAT(nd.hostname, '.', dom.name) AS fqdn, + dom.name as dom_name, + nd.system_id, + nd.node_type, + nd.domain_id, + nd.address_ttl, + dom.ttl, + iia.staticipaddress_id AS node_sip_id, + dom2.id AS dom2_id + FROM maasserver_interface_ip_addresses AS iia + JOIN maasserver_interface AS iface ON + iia.interface_id = iface.id + JOIN maasserver_node AS nd ON + iface.node_id = nd.id + JOIN maasserver_domain AS dom ON + nd.domain_id = dom.id + LEFT JOIN maasserver_domain AS dom2 ON + CONCAT(nd.hostname, '.', dom.name) = dom2.name + ) AS node ON + node_sip_id = staticip.id + WHERE + staticip.ip IS NOT NULL AND + host(staticip.ip) != '' AND + ( + staticip.alloc_type = %s OR + node.fqdn IS NULL OR + dnsrr IS NOT NULL + )""" + where_clause + """ + """ + default_domain = Domain.objects.get_default_domain() + mapping = defaultdict(HostnameIPMapping) + cursor = connection.cursor() + query_parms = [IPADDRESS_TYPE.USER_RESERVED] + query_parms + cursor.execute(sql_query, query_parms) + for (fqdn, system_id, node_type, ttl, + ip) in cursor.fetchall(): + if fqdn is None or fqdn == '': + fqdn = "%s.%s" % ( + get_ip_based_hostname(ip), default_domain.name) + mapping[fqdn].node_type = node_type + mapping[fqdn].system_id = system_id + mapping[fqdn].ttl = ttl + mapping[fqdn].ips.add(ip) + return mapping @asynchronous def _async_find_free_ip(self, *args, **kwargs): @@ -321,7 +416,7 @@ node.boot_interface_id = parent.id ), False - ) as is_boot + ) AS is_boot FROM maasserver_interface AS interface LEFT OUTER JOIN maasserver_interfacerelationship AS rel ON @@ -330,7 +425,7 @@ rel.parent_id = parent.id JOIN maasserver_node AS node ON node.id = interface.node_id - JOIN maasserver_domain as domain ON + JOIN maasserver_domain AS domain ON domain.id = node.domain_id JOIN maasserver_interface_ip_addresses AS link ON link.interface_id = interface.id @@ -344,15 +439,14 @@ # domains. domain2.name will be non-null if this host's fqdn is the # name of a domain in MAAS. sql_query += """ - LEFT JOIN maasserver_domain as domain2 ON + LEFT JOIN maasserver_domain AS domain2 ON /* Pick up another copy of domain looking for instances of * nodes a the top of a domain. - */ - domain2.name = CONCAT(node.hostname, '.', domain.name) + */ domain2.name = CONCAT(node.hostname, '.', domain.name) WHERE - (domain2.name IS NOT NULL OR node.domain_id = %s) AND + (domain2.id = %s OR node.domain_id = %s) AND """ - query_parms = [domain_or_subnet.id, ] + query_parms = [domain_or_subnet.id, domain_or_subnet.id, ] else: # For subnets, we need ALL the names, so that we can correctly # identify which ones should have the FQDN. dns/zonegenerator.py @@ -413,7 +507,7 @@ maasserver_interface AS interface JOIN maasserver_node AS node ON node.id = interface.node_id - JOIN maasserver_domain as domain ON + JOIN maasserver_domain AS domain ON domain.id = node.domain_id JOIN maasserver_interface_ip_addresses AS link ON link.interface_id = interface.id @@ -423,14 +517,14 @@ if isinstance(domain_or_subnet, Domain): # This logic is similar to the logic in sql_query above. iface_sql_query += """ - LEFT JOIN maasserver_domain as domain2 ON + LEFT JOIN maasserver_domain AS domain2 ON /* Pick up another copy of domain looking for instances of * the name as the top of a domain. */ domain2.name = CONCAT( interface.name, '.', node.hostname, '.', domain.name) WHERE - (domain2.name IS NOT NULL OR node.domain_id = %s) AND + (domain2.id = %s OR node.domain_id = %s) AND """ else: # For subnets, we need ALL the names, so that we can correctly @@ -450,7 +544,7 @@ """ # We get user reserved et al mappings first, so that we can overwrite # TTL as we process the return from the SQL horror above. - mapping = self._get_user_reserved_mappings(domain_or_subnet) + mapping = self._get_special_mappings(domain_or_subnet, raw_ttl) # All of the mappings that we got mean that we will only want to add # addresses for the boot interface (is_boot == True). iface_is_boot = defaultdict(bool, { @@ -477,7 +571,7 @@ # Next, get all the addresses, on all the interfaces, and add the ones # that are not already present on the FQDN as $IFACE.$FQDN. Exclude # any discovered addresses once there are any non-discovered addresses. - cursor.execute(iface_sql_query, (domain_or_subnet.id,)) + cursor.execute(iface_sql_query, query_parms) for (fqdn, system_id, node_type, ttl, ip, iface_name, assigned) in cursor.fetchall(): if assigned: diff -Nru maas-2.1.1+bzr5544/src/maasserver/models/tests/test_interface.py maas-2.1.3+bzr5573/src/maasserver/models/tests/test_interface.py --- maas-2.1.1+bzr5544/src/maasserver/models/tests/test_interface.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/models/tests/test_interface.py 2016-12-21 07:56:19.000000000 -0500 @@ -2045,6 +2045,17 @@ "IP address is not in the given subnet '%s'." % subnet, str(error)) + def test__AUTO_link_sets_vlan_if_vlan_undefined(self): + interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) + network = factory.make_ipv4_network() + subnet = factory.make_Subnet( + vlan=interface.vlan, cidr=str(network.cidr)) + interface.vlan = None + interface.save() + interface.link_subnet(INTERFACE_LINK_TYPE.AUTO, subnet) + interface = reload_object(interface) + self.assertThat(interface.vlan, Equals(subnet.vlan)) + def test__STATIC_not_allowed_if_ip_address_in_dynamic_range(self): interface = factory.make_Interface(INTERFACE_TYPE.PHYSICAL) subnet = factory.make_ipv4_Subnet_with_IPRanges(vlan=interface.vlan) diff -Nru maas-2.1.1+bzr5544/src/maasserver/models/tests/test_node.py maas-2.1.3+bzr5573/src/maasserver/models/tests/test_node.py --- maas-2.1.1+bzr5544/src/maasserver/models/tests/test_node.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/models/tests/test_node.py 2016-12-21 07:56:19.000000000 -0500 @@ -595,7 +595,12 @@ super().setUp() # Patch out gethostname and get_mac_addresses. self.patch_autospec(node_module, "gethostname") - node_module.gethostname.return_value = factory.make_name("host") + hostname = factory.make_name('host') + # Bug#1614584: make sure that we handle the case where gethostname() + # returns an FQDN, instead of a domainless hostname. + if factory.pick_bool(): + hostname += ".%s" % factory.make_name('domain') + node_module.gethostname.return_value = hostname self.patch_autospec(node_module, "get_mac_addresses") node_module.get_mac_addresses.return_value = [] diff -Nru maas-2.1.1+bzr5544/src/maasserver/models/tests/test_staticipaddress.py maas-2.1.3+bzr5573/src/maasserver/models/tests/test_staticipaddress.py --- maas-2.1.1+bzr5544/src/maasserver/models/tests/test_staticipaddress.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/models/tests/test_staticipaddress.py 2016-12-21 07:56:19.000000000 -0500 @@ -41,6 +41,7 @@ MAASServerTestCase, MAASTransactionServerTestCase, ) +from maasserver.utils.dns import get_ip_based_hostname from maasserver.utils.orm import ( reload_object, RetryTransaction, @@ -397,20 +398,21 @@ alloc_type=IPADDRESS_TYPE.STICKY, ip=factory.pick_ip_in_Subnet(subnet), subnet=subnet, interface=boot_interface) - ifaces = node.interface_set.exclude(interface=boot_interface) + iface2 = node.interface_set.exclude(id=boot_interface.id).first() sip2 = factory.make_StaticIPAddress( alloc_type=IPADDRESS_TYPE.STICKY, ip=factory.pick_ip_in_Subnet(subnet), - subnet=subnet, interface=ifaces[0]) + subnet=subnet, interface=iface2) mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) - self.assertEqual({ + expected = { full_hostname: HostnameIPMapping( node.system_id, 30, {staticip.ip}, node.node_type), - "%s.%s" % (ifaces[0].name, full_hostname): + "%s.%s" % (iface2.name, full_hostname): HostnameIPMapping( node.system_id, 30, {sip2.ip}, node.node_type), - }, mapping) + } + self.assertEqual(expected, mapping) def make_mapping(self, node, raw_ttl=False): if raw_ttl or node.address_ttl is not None: @@ -452,7 +454,6 @@ actual = StaticIPAddress.objects.get_hostname_ip_mapping(dom) self.assertItemsEqual(expected_mapping, actual) - @skip("XXX: GavinPanella 2016-02-24 bug=1549397: Fails spuriously.") def test_get_hostname_ip_mapping_returns_raw_ttl(self): # We create 2 domains, one with a ttl, one withoout. # Within each domain, create a node with an address_ttl, and one @@ -946,6 +947,7 @@ class TestUserReservedStaticIPAddress(MAASServerTestCase): def test_user_reserved_addresses_have_default_hostnames(self): + # Reserved IPs get default hostnames when none are given. subnet = factory.make_Subnet() num_ips = randint(3, 5) ips = [ @@ -953,11 +955,13 @@ subnet=subnet, alloc_type=IPADDRESS_TYPE.USER_RESERVED) for _ in range(num_ips) ] - mappings = StaticIPAddress.objects._get_user_reserved_mappings( + mappings = StaticIPAddress.objects._get_special_mappings( subnet) self.expectThat(mappings, HasLength(len(ips))) def test_user_reserved_addresses_included_in_get_hostname_ip_mapping(self): + # Generate several IPs, with names in domain0, and make sure they don't + # show up in domain1. num_ips = randint(3, 5) domain0 = Domain.objects.get_default_domain() domain1 = factory.make_Domain() @@ -973,6 +977,8 @@ self.expectThat(mappings, HasLength(0)) def test_user_reserved_addresses_included_in_correct_domains(self): + # Generate addresses in two domains, and make sure that they wind up in + # the right domains, and not in other domains. domain0 = Domain.objects.get_default_domain() domain1 = factory.make_Domain() domain2 = factory.make_Domain() @@ -997,6 +1003,138 @@ mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain2) self.expectThat(mappings, HasLength(len(ips2))) + def test_dns_resources_only_have_correct_domain(self): + # Create two DNS resources pointing to the same IP, and make sure that + # they only get the forward mapping that they should have, but get both + # reverse mappings, as is proper. + domain0 = Domain.objects.get_default_domain() + domain1 = factory.make_Domain() + domain2 = factory.make_Domain() + subnet = factory.make_Subnet() + ip = factory.make_StaticIPAddress(subnet=subnet) + name1 = factory.make_name('label') + name2 = factory.make_name('label') + factory.make_DNSResource(name=name1, ip_addresses=[ip], domain=domain1) + factory.make_DNSResource(name=name2, ip_addresses=[ip], domain=domain2) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain0) + self.expectThat(mappings, HasLength(0)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain1) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings.get('%s.%s' % (name1, domain1.name)), + HostnameIPMapping(None, 30, {ip.ip}, None)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain2) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings.get('%s.%s' % (name2, domain2.name)), + HostnameIPMapping(None, 30, {ip.ip}, None)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) + self.assertEqual( + mappings, { + "%s.%s" % (name1, domain1.name): HostnameIPMapping( + None, 30, {ip.ip}, None), + "%s.%s" % (name2, domain2.name): HostnameIPMapping( + None, 30, {ip.ip}, None)}) + + def test_user_reserved_IP_on_node_or_default_domain_not_both(self): + # ip1 gets assigned to an interface on a node in domain1 + # ip2 is not assigned to anything. + # ip1 should show up for the node, in domain1, and the subnet. + # ip2 should show up in the default domain, and the subnet. + domain0 = Domain.objects.get_default_domain() + domain1 = factory.make_Domain() + subnet = factory.make_Subnet() + ip1 = factory.make_StaticIPAddress(subnet=subnet) + ip2 = factory.make_StaticIPAddress(subnet=subnet) + name2 = "%s.%s" % (get_ip_based_hostname(ip2.ip), domain0.name) + node = factory.make_Node( + interface=True, + domain=domain1, + vlan=subnet.vlan) + node.interface_set.first().ip_addresses.add(ip1) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain0) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings, { + name2: HostnameIPMapping(None, 30, {ip2.ip}, None)}) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain1) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings.get(node.fqdn), + HostnameIPMapping(node.system_id, 30, {ip1.ip}, node.node_type)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) + self.assertEqual( + mappings, { + node.fqdn: HostnameIPMapping( + node.system_id, 30, {ip1.ip}, node.node_type), + name2: HostnameIPMapping(None, 30, {ip2.ip}, None)}) + + def test_node_glue_correctly_generated(self): + # Given node "foo.bar.com" and a "foo.bar.com" domain (in addition to + # "bar.com"), with an IP associated with it, make sure that both + # domains are correctly populated. + domain0 = Domain.objects.get_default_domain() + parent = factory.make_Domain(name=factory.make_name('parent')) + name = factory.make_name('child') + domain = factory.make_Domain(name="%s.%s" % (name, parent.name)) + subnet = factory.make_Subnet() + ip = factory.make_StaticIPAddress(subnet=subnet) + node = factory.make_Node( + interface=True, + domain=parent, + hostname=name, + vlan=subnet.vlan) + node.interface_set.first().ip_addresses.add(ip) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain0) + self.expectThat(mappings, HasLength(0)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(parent) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings.get(node.fqdn), + HostnameIPMapping(node.system_id, 30, {ip.ip}, node.node_type)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings.get(node.fqdn), + HostnameIPMapping(node.system_id, 30, {ip.ip}, node.node_type)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) + self.assertEqual( + mappings, { + node.fqdn: HostnameIPMapping( + node.system_id, 30, {ip.ip}, node.node_type)}) + + def test_dnsrr_glue_correctly_generated(self): + # Given DNSResource "foo.bar.com" and a "foo.bar.com" domain (in + # addition to "bar.com"), with an IP associated with it, make sure that + # both domains are correctly populated. + domain0 = Domain.objects.get_default_domain() + parent = factory.make_Domain(name=factory.make_name('parent')) + name = factory.make_name('child') + domain = factory.make_Domain(name="%s.%s" % (name, parent.name)) + subnet = factory.make_Subnet() + ip = factory.make_StaticIPAddress(subnet=subnet) + dnsrr = factory.make_DNSResource( + domain=domain, + name='@', + ip_addresses=[ip]) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain0) + self.expectThat(mappings, HasLength(0)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(parent) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings.get(dnsrr.fqdn), + HostnameIPMapping(None, 30, {ip.ip}, None)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(domain) + self.expectThat(mappings, HasLength(1)) + self.assertEqual( + mappings.get(dnsrr.fqdn), + HostnameIPMapping(None, 30, {ip.ip}, None)) + mappings = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) + self.assertEqual( + mappings, { + dnsrr.fqdn: HostnameIPMapping( + None, 30, {ip.ip}, None)}) + class TestRenderJSON(MAASServerTestCase): diff -Nru maas-2.1.1+bzr5544/src/maasserver/preseed.py maas-2.1.3+bzr5573/src/maasserver/preseed.py --- maas-2.1.1+bzr5544/src/maasserver/preseed.py 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/preseed.py 2016-12-21 07:56:19.000000000 -0500 @@ -17,6 +17,7 @@ from collections import namedtuple import json import os.path +from pipes import quote from urllib.parse import ( urlencode, urlparse, @@ -61,7 +62,10 @@ from metadataserver.models import NodeKey from metadataserver.user_data.snippets import get_snippet_context from provisioningserver.drivers.osystem.ubuntu import UbuntuOS -from provisioningserver.logger import get_maas_logger +from provisioningserver.logger import ( + get_maas_logger, + LegacyLogger, +) from provisioningserver.rpc.exceptions import NoConnectionsAvailable from provisioningserver.utils import typed from provisioningserver.utils.url import compose_URL @@ -71,6 +75,8 @@ maaslog = get_maas_logger("preseed") +log = LegacyLogger() + GENERIC_FILENAME = 'generic' @@ -416,7 +422,41 @@ get_node_preseed_context( node, osystem, series, rack_controller=rack_controller)) context.update(get_curtin_context(node, rack_controller=rack_controller)) - return template.substitute(**context) + deprecated_context_variables = [ + 'main_archive_hostname', 'main_archive_directory', + 'ports_archive_hostname', 'ports_archive_directory', + 'enable_http_proxy', 'http_proxy'] + deprecated_config_variables = [] + for var in deprecated_context_variables: + if var not in context: + deprecated_context_variables.remove(var) + context.update(get_node_deprecated_preseed_context()) + config = yaml.load(template.substitute(**context)) + # Remove deprecated config from the curtin preseed. + if 'power_state' in config: + del config['power_state'] + deprecated_config_variables.append('power_state') + if 'apt_proxy' in config: + deprecated_config_variables.append('apt_proxy') + del config['apt_proxy'] + if 'apt_mirrors' in config: + deprecated_config_variables.append('apt_mirrors') + del config['apt_mirrors'] + if deprecated_context_variables: + log.warn( + "WARNING: '%s' contains deprecated preseed " + "variables. Please remove: %s" % ( + template.name, ", ".join(deprecated_context_variables))) + if deprecated_config_variables: + log.warn( + "WARNING: '%s' contains deprecated preseed " + "configuration. Please remove: %s" % ( + template.name, ", ".join(deprecated_config_variables))) + # Precise does not support cloud-init performing the reboot, so curtin + # must have this statement. + if node.distro_series == "precise": + config['power_state'] = {'mode': 'reboot'} + return yaml.dump(config) def get_curtin_context(node, rack_controller=None): @@ -590,8 +630,11 @@ """Return a singleton containing methods to escape various formats used in the preseed templates. """ - Escape = namedtuple('Escape', 'json') - return Escape(json=json.dumps) + # Bug#1642996: We need to keep escape.shell in 2.X, for backwards + # compatibility. Any bugs filed about how it doesn't work should be marked + # as a dup of Bug#1643595, and the user told to change to escape.json. + Escape = namedtuple('Escape', ['json', 'shell']) + return Escape(json=json.dumps, shell=quote) class PreseedTemplate(tempita.Template): @@ -710,6 +753,30 @@ } +def get_node_deprecated_preseed_context(): + """Return the node-dependent context dictionary to be used to render + preseed template. This includes all the context variables that have + been deprecated, but allows for backwards compatibility for those + preseeds that still contain old variables. + + :return: The context dictionary. + :rtype: dict. + """ + main_archive_hostname, main_archive_directory = get_netloc_and_path( + PackageRepository.get_main_archive().url) + ports_archive_hostname, ports_archive_directory = get_netloc_and_path( + PackageRepository.get_ports_archive().url) + + return { + 'main_archive_hostname': main_archive_hostname, + 'main_archive_directory': main_archive_directory, + 'ports_archive_hostname': ports_archive_hostname, + 'ports_archive_directory': ports_archive_directory, + 'enable_http_proxy': Config.objects.get_config('enable_http_proxy'), + 'http_proxy': Config.objects.get_config('http_proxy'), + } + + def render_enlistment_preseed( prefix, osystem='', release='', rack_controller=None): """Return the enlistment preseed. diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/account.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/account.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/account.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/account.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,163 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/add-logical-volume.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/add-logical-volume.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/add-logical-volume.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/add-logical-volume.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,91 +1 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/add-partition.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/add-partition.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/add-partition.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/add-partition.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,117 +1 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/add.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/add.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/add.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/add.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,24 +1 @@ - - - - remove icon copy - Created with Sketch. - - - - - - - - - - - - - - - - - - - \ No newline at end of file +add \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/cross_orange.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/cross_orange.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/cross_orange.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/cross_orange.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,10 +1 @@ - - - - Imported Layers - Created with Sketch. - - - - - \ No newline at end of file +Imported Layers \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/cross-orange.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/cross-orange.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/cross-orange.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/cross-orange.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,10 +1 @@ - - - - Imported Layers - Created with Sketch. - - - - - \ No newline at end of file +Imported Layers \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/cross.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/cross.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/cross.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/cross.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,18 +1 @@ - - - - Imported Layers Copy - Created with Sketch. - - - - - - - - - - - - - \ No newline at end of file +Imported Layers Copy \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/debug.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/debug.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/debug.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/debug.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,18 +1 @@ - - - - debug icon 8@2x - Created with Sketch. - - - - - - - - - - - - - \ No newline at end of file +debug icon 8@2x \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/delete.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/delete.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/delete.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/delete.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,178 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - +remove \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/edit.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/edit.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/edit.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/edit.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,168 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - +edit \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error_colour_black.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error_colour_black.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error_colour_black.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error_colour_black.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,117 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error_colour_white.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error_colour_white.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error_colour_white.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error_colour_white.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,117 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error_mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error_mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error_mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error_mono.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,96 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error-mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error-mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error-mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error-mono.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,96 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/error.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/error.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,117 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - +error \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/green-tick.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/green-tick.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/green-tick.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/green-tick.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,20 +1 @@ - - - textfield-saved - Created with Sketch. - - - - - - - - - - - - - - - - +textfield-saved \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help_colour_black.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help_colour_black.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help_colour_black.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help_colour_black.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,104 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help_colour_white.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help_colour_white.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help_colour_white.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help_colour_white.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,104 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help_mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help_mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help_mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help_mono.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,96 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help-mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help-mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help-mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help-mono.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,96 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/help.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/help.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,104 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - +help \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/info-mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/info-mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/info-mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/info-mono.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,96 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/information_colour_white.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/information_colour_white.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/information_colour_white.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/information_colour_white.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,113 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/information_mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/information_mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/information_mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/information_mono.svg 1969-12-31 19:00:00.000000000 -0500 @@ -1,96 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/info.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/info.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/info.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/info.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,113 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - +info \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/logical-volume.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/logical-volume.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/logical-volume.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/logical-volume.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,91 +1 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/magnifying_glass.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/magnifying_glass.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/magnifying_glass.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/magnifying_glass.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,10 +1 @@ - - - - Untitled 2 - Created with Sketch. - - - - - \ No newline at end of file +magnifying_glass \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/mount.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/mount.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/mount.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/mount.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,81 +1 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/partition.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/partition.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/partition.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/partition.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,117 +1 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/power-error.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/power-error.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/power-error.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/power-error.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,12 +1 @@ - - - - power-error - Created with Sketch. - - - - - - - \ No newline at end of file +power-error \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/power-off.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/power-off.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/power-off.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/power-off.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,12 +1 @@ - - - - power-off - Created with Sketch. - - - - - - - \ No newline at end of file +power-off \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/power-on.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/power-on.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/power-on.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/power-on.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,12 +1 @@ - - - - power-on - Created with Sketch. - - - - - - - \ No newline at end of file +power-on \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/remove.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/remove.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/remove.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/remove.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,12 +1 @@ - - - - filter-remove - Created with Sketch. - - - - - - - \ No newline at end of file +remove \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/settings.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/settings.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/settings.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/settings.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,174 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success_colour_black.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success_colour_black.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success_colour_black.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success_colour_black.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,103 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success_colour_white.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success_colour_white.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success_colour_white.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success_colour_white.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,103 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success_mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success_mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success_mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success_mono.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,95 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success-mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success-mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success-mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success-mono.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,95 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/success.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/success.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,103 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/sync.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/sync.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/sync.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/sync.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,165 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/system-shutdown.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/system-shutdown.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/system-shutdown.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/system-shutdown.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,148 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/tags.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/tags.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/tags.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/tags.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,117 +1 @@ - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/tick.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/tick.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/tick.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/tick.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,20 +1 @@ - - - textfield-saved - Created with Sketch. - - - - - - - - - - - - - - - - +textfield-saved \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/tooltip.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/tooltip.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/tooltip.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/tooltip.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,125 +1 @@ - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/unmount.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/unmount.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/unmount.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/unmount.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,81 +1 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning_colour_black.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning_colour_black.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning_colour_black.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning_colour_black.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,114 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning_colour_white.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning_colour_white.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning_colour_white.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning_colour_white.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,123 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning_mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning_mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning_mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning_mono.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,96 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning-mono.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning-mono.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning-mono.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning-mono.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,96 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - + \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning.svg maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning.svg --- maas-2.1.1+bzr5544/src/maasserver/static/assets/images/icons/warning.svg 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/assets/images/icons/warning.svg 2016-12-21 07:56:19.000000000 -0500 @@ -1,123 +1 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - +warning \ No newline at end of file diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/css/build.css maas-2.1.3+bzr5573/src/maasserver/static/css/build.css --- maas-2.1.1+bzr5544/src/maasserver/static/css/build.css 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/css/build.css 2016-12-21 07:56:19.000000000 -0500 @@ -1 +1 @@ -*{font-smoothing:subpixel-antialiased;box-sizing:border-box}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{font-smoothing:subpixel-antialiased;font-size:1em;display:inline-block;width:100%;margin:0;padding:11px 24px;text-align:center;text-decoration:none;border:0;border-radius:2px;outline:none;font-weight:300;cursor:pointer}button:hover,input[type='button']:hover,input[type='reset']:hover,input[type='submit']:hover,.button--primary:hover,.button--secondary:hover,.button--neutral:hover,.button--inline-neutral:hover,.button--positive:hover,.button--inline-positive:hover,.button--destructive:hover,.button--inline-destructive:hover,.button--base:hover,.button--inline-base:hover{text-decoration:none}@media only screen and (min-width: 768px){button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{width:auto}}.clearfix{display:block}.clearfix:after{clear:both;content:'\0020';display:block;height:0;overflow:hidden;visibility:hidden}textarea,input[type='text'],input[type='number'],input[type='search'],input[type='password'],input[type='email'],input[type='url'],input[type='tel'],select{-webkit-appearance:textfield;border-radius:2px;border:1px solid #cdcdcd;box-shadow:inset 0 1px 2px rgba(0,0,0,0.12);font-size:16px;margin:0;outline:none;padding:.6956522em .869565em;vertical-align:baseline;font-weight:300}textarea:active,input[type='text']:active,input[type='number']:active,input[type='search']:active,input[type='password']:active,input[type='email']:active,input[type='url']:active,input[type='tel']:active,select:active,textarea:focus,input[type='text']:focus,input[type='number']:focus,input[type='search']:focus,input[type='password']:focus,input[type='email']:focus,input[type='url']:focus,input[type='tel']:focus,select:focus{border-color:#888;outline:none}textarea::placeholder,input[type='text']::placeholder,input[type='number']::placeholder,input[type='search']::placeholder,input[type='password']::placeholder,input[type='email']::placeholder,input[type='url']::placeholder,input[type='tel']::placeholder,select::placeholder{color:contrast-friendly-search-color(#f7f7f7)}textarea[disabled="disabled"],input[disabled="disabled"][type='text'],input[disabled="disabled"][type='number'],input[disabled="disabled"][type='search'],input[disabled="disabled"][type='password'],input[disabled="disabled"][type='email'],input[disabled="disabled"][type='url'],input[disabled="disabled"][type='tel'],select[disabled="disabled"]{opacity:.5}input[type='checkbox'],input[type='radio']{border-radius:2px;border:1px solid #cdcdcd;box-shadow:inset 0 1px 2px rgba(0,0,0,0.12);height:14px;padding:0;vertical-align:middle;width:14px}.inner-wrapper{margin:0 auto;max-width:1030px}[class*='-col'].last-col,[class*='-col'] [class*='-col'].last-col{margin-right:0}.one-col,.two-col,.three-col,.four-col,.five-col,.six-col,.seven-col,.eight-col,.nine-col,.ten-col,.eleven-col,.twelve-col{clear:none;margin-bottom:20px;margin-right:0;padding:0;display:inline-block;position:relative;width:100%}@media only screen and (min-width: 768px){.one-col,.two-col,.three-col,.four-col,.five-col,.six-col,.seven-col,.eight-col,.nine-col,.ten-col,.eleven-col,.twelve-col{float:left}}.list-ticks,.list{list-style:none;margin-left:0}.list-ticks li,.list li{border-bottom:1px dotted #888;margin-bottom:0;padding:10px 0}.list-ticks li:last-of-type,.list li:last-of-type,.list-ticks .last-item,.list .last-item{border:0;margin-bottom:0;padding-bottom:0}.list-ticks li{background-repeat:no-repeat;background-position:0 1em;padding-left:25px}.box{background:#fff;border:1px solid #cdcdcd;padding:1.25em 20px}@media only screen and (min-width: 768px){.equal-height,.equal-height--vertical-divider{display:flex;flex-wrap:wrap;flex-direction:row;width:100%}.equal-height .equal-height__item,.equal-height--vertical-divider .equal-height__item{display:flex;flex:auto;flex-direction:column}}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{font-size:1em;box-sizing:border-box;width:100%;margin:0;padding:11px 24px;text-align:center;text-decoration:none;border-radius:2px;outline:none;font-weight:300;cursor:pointer;line-height:1em}button:disabled,input[type='button']:disabled,input[type='reset']:disabled,input[type='submit']:disabled,.button--primary:disabled,.button--secondary:disabled,.button--neutral:disabled,.button--inline-neutral:disabled,.button--positive:disabled,.button--inline-positive:disabled,.button--destructive:disabled,.button--inline-destructive:disabled,.button--base:disabled,.button--inline-base:disabled,button.button--disabled,input.button--disabled[type='button'],input.button--disabled[type='reset'],input.button--disabled[type='submit'],.button--disabled.button--primary,.button--disabled.button--secondary,.button--disabled.button--neutral,.button--disabled.button--inline-neutral,.button--disabled.button--positive,.button--disabled.button--inline-positive,.button--disabled.button--destructive,.button--disabled.button--inline-destructive,.button--disabled.button--base,.button--disabled.button--inline-base{opacity:.5;cursor:default}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{line-height:20px;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;user-select:none;padding:8px 14px;height:36px;box-sizing:border-box}button[disabled],input[disabled][type='button'],input[disabled][type='reset'],input[disabled][type='submit'],[disabled].button--primary,[disabled].button--secondary,[disabled].button--neutral,[disabled].button--inline-neutral,[disabled].button--positive,[disabled].button--inline-positive,[disabled].button--destructive,[disabled].button--inline-destructive,[disabled].button--base,[disabled].button--inline-base,button.button--disabled,input.button--disabled[type='button'],input.button--disabled[type='reset'],input.button--disabled[type='submit'],.button--disabled.button--primary,.button--disabled.button--secondary,.button--disabled.button--neutral,.button--disabled.button--inline-neutral,.button--disabled.button--positive,.button--disabled.button--inline-positive,.button--disabled.button--destructive,.button--disabled.button--inline-destructive,.button--disabled.button--base,.button--disabled.button--inline-base{pointer-events:none;opacity:.5}button.button--inline,input.button--inline[type='button'],input.button--inline[type='reset'],input.button--inline[type='submit'],.button--inline.button--primary,.button--inline.button--secondary,.button--inline.button--neutral,.button--inline.button--inline-neutral,.button--inline.button--positive,.button--inline.button--inline-positive,.button--inline.button--destructive,.button--inline.button--inline-destructive,.button--inline.button--base,.button--inline.button--inline-base{display:inline-block;width:auto}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,ol,ul,li,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;margin:0;padding:0;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,nav,section{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none}[hidden]{display:none}html{font-size:100%;overflow-y:scroll;text-size-adjust:100%}blockquote,q{quotes:none}legend{border:0}figure{margin:0}abbr,acronym{cursor:help}.link-arrow:after{content:'\0000a0›'}nav ul li h2 a:after{content:'\0000a0›'}nav ul li a:after,.carousel ul li a:after,ul li p a:after{content:''}svg:not(:root){overflow:hidden}img{border:0;height:auto;max-width:100%}img .left{margin-right:20px}img .right{margin-left:20px}.middle img{vertical-align:middle;margin-top:4em}ins{background:#fffbeb;text-decoration:none}.left{float:left}.right{float:right}.no-border{border:0}.link-top{font-size:.875em;clear:both;margin-bottom:40px;margin-top:-40px}.link-top a{background:#fff;margin-right:10px;margin-top:-35px;padding:5px;float:right}.caps{text-transform:uppercase}.touch-border,.touch-bottom{float:left}@media only screen and (min-width: 768px){.touch-border,.touch-bottom{margin-bottom:-20px}}@media only screen and (min-width: 1440px){.touch-border,.touch-bottom{margin-bottom:-40px}}.accessibility-aid,.off-left{position:absolute;left:-999em}.external{background-size:.7em .7em;padding-right:.9em;background-image:url("https://assets.ubuntu.com/v1/f24a8aa0-external-link-orange.svg");background-position:100% top;background-repeat:no-repeat}.no-svg .external{background-image:url("https://assets.ubuntu.com/v1/f24a8aa0-external-link-orange.svg")}.text-center,.align-center{text-align:center}.no-margin-bottom{margin-bottom:0}.no-padding-bottom{padding-bottom:0}.pull-bottom-right{float:right;margin-right:-10px}@media only screen and (min-width: 768px){.pull-bottom-right{margin-right:-40px;margin-bottom:-40px}}@media only screen and (min-width: 1440px){.pull-bottom-right{margin-right:-40px;margin-bottom:-60px}}.pull-bottom-left{float:left;margin-left:-10px}@media only screen and (min-width: 768px){.pull-bottom-left{margin-left:-40px;margin-bottom:-40px}}@media only screen and (min-width: 1440px){.pull-bottom-left{margin-left:-40px;margin-bottom:-60px}}@media only screen and (max-width: 767px){.priority-0,.not-for-small{display:none}}@media only screen and (min-width: 768px){.for-mobile,.for-small{display:none}}.video-container{position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden}.video-container iframe{position:absolute;top:0;left:0;width:100%;height:100%}.video-container+.video-title{margin-top:20px}.pull-right{float:right;margin-right:-40px}@media only screen and (max-width: 768px){.pull-right{margin-bottom:20px}}.pull-left{margin-left:-40px}.for-tablet,.for-medium{display:none}@media only screen and (min-width: 768px){.for-tablet,.for-medium{display:block}}@media only screen and (min-width: 768px) and (max-width: 1440px){.med-six-col .three-col{width:48.93617%}.med-six-col .three-col:nth-of-type(2n){margin-right:0}}blockquote{margin:0}blockquote>p{font-size:1em;margin:0 0 .4em}blockquote small{font-size:.8125em;line-height:1.4}.pull-quote{text-indent:0}.pull-quote p{font-size:1.5em;line-height:1.3;margin-left:.4em;padding-left:10px;padding-right:10px;text-indent:-.4em}.pull-quote p span{color:#f7f7f7;font-weight:bold;left:-5px;line-height:0;position:relative}.pull-quote p span:last-of-type{left:5px}.pull-quote p cite{display:block;font-size:.75em;font-weight:300;margin:10px 0 0;text-indent:0}@media only screen and (min-width: 768px){.pull-quote p{padding-left:0;padding-right:0;text-indent:-.7em}.pull-quote p span{top:5px;font-size:1.391304348em}.pull-quote p cite{margin-left:0;text-indent:0}}@media only screen and (min-width: 1440px){.pull-quote p span{top:10px}}.row-quote{border-radius:0}.row-quote blockquote{border-radius:4px;margin:0;padding:0}.row-quote blockquote p{color:#333;line-height:1.3;margin-bottom:.75em;padding-left:10px;padding-right:10px;text-indent:0}.row-quote blockquote span{color:#f7f7f7;font-weight:bold;left:-5px;line-height:0;position:relative}.row-quote blockquote span+span{left:5px}.row-quote blockquote cite{color:#333;font-size:.75em;font-style:normal;margin-bottom:0;text-indent:0}@media only screen and (min-width: 768px){.row-quote{text-indent:-.4em}.row-quote blockquote{text-indent:-7px}.row-quote blockquote p{font-size:1.5em;padding-left:0;padding-right:0;text-indent:-.4em}.row-quote blockquote span{top:5px;font-size:1.391304348em}.row-quote blockquote cite{margin-left:0;text-indent:0}}@media only screen and (min-width: 1440px){.row-quote blockquote{padding:0 80px 20px;text-indent:-10px}.row-quote blockquote p span{top:10px}}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary{color:#fff;background-color:#dd4814}button:hover,input[type='button']:hover,input[type='reset']:hover,input[type='submit']:hover,.button--primary:hover{background-color:#c03f11}.button--secondary{color:#dd4814;background-color:#fff;border:1px solid #cdcdcd}.button--secondary:hover{background-color:#efefef}@media only screen and (min-width: 620px){.nav-secondary{border:1px solid #d2d2d2;border-top:0}}.nav-secondary .breadcrumb{margin-bottom:0;margin-left:0;padding-top:2px;padding-bottom:3px}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb{float:left;margin-left:4px}}.nav-secondary .breadcrumb li{color:#fff;display:inline-block;margin:0;width:100%}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li{display:inline-block;width:auto}}.nav-secondary .breadcrumb li:first-of-type{border-bottom:1px solid #d2d2d2}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li:first-of-type{border-bottom:0}}.nav-secondary .breadcrumb li a{color:#888;font-size:1em;display:inline-block;width:100%;margin-right:0;padding:8px 10px;text-decoration:none}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li a{font-size:.875em}}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li .breadcrumb-link,.nav-secondary .breadcrumb li .breadcrumb-link--second-level,.nav-secondary .breadcrumb li .active{display:inline-block;float:none;width:auto}}.nav-secondary .breadcrumb li .breadcrumb-link:after,.nav-secondary .breadcrumb li .breadcrumb-link--second-level:after{content:'\203A';display:inline-block;height:5px;margin-left:8px;position:absolute;width:5px}.nav-secondary .second-level-nav,.nav-secondary .third-level-nav{display:none;overflow:hidden;width:100%;margin:0;padding-top:10px;padding-bottom:10px;border-bottom:1px solid #d2d2d2}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav,.nav-secondary .third-level-nav{display:inline;float:none;margin-bottom:0;padding:0;width:auto;border-bottom:0}}.nav-secondary .second-level-nav li,.nav-secondary .third-level-nav li{margin:0}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav li,.nav-secondary .third-level-nav li{float:none;width:auto}}.nav-secondary .second-level-nav li a,.nav-secondary .third-level-nav li a{font-size:.875em;display:block;width:100%;height:100%;padding:10px;color:#333}@media only screen and (max-width: 620px){.nav-secondary .second-level-nav li a.active,.nav-secondary .third-level-nav li a.active{font-weight:500}}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav li a,.nav-secondary .third-level-nav li a{padding:8px 10px 0}.nav-secondary .second-level-nav li a.active,.nav-secondary .third-level-nav li a.active{color:#f7f7f7}}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav li{width:auto}.nav-secondary .second-level-nav li .active,.nav-secondary .second-level-nav li .breadcrumb-link--second-level{color:#888}.nav-secondary .second-level-nav li .third-level-nav li a{color:#333}.nav-secondary .second-level-nav li .third-level-nav li a:hover{color:#f7f7f7}.nav-secondary .second-level-nav li .third-level-nav li .active{color:#f7f7f7;float:none}.nav-secondary .second-level-nav li .third-level-nav li .active:after{content:''}}.third-level-nav li a:hover{color:#f7f7f7}.third-level-nav li .active{color:#f7f7f7;float:none}@media only screen and (max-width: 619px){#breadcrumbs:hover .second-level-nav,#breadcrumbs:hover .third-level-nav,#breadcrumbs:hover .active{border:0;display:block;overflow:visible;float:left;width:100%;position:relative;clear:both}#breadcrumbs:hover li{border:0;float:left}#breadcrumbs:hover .second-level-nav{border-top:1px solid #d2d2d2}#breadcrumbs:hover .second-level-nav li{float:left}#breadcrumbs:hover .third-level-nav{border-bottom:1px solid #d2d2d2;padding-top:0}#breadcrumbs:hover .third-level-nav li{width:50%;display:block;padding-left:10px}}label{cursor:pointer;display:block;margin-bottom:4px}label.has-error{color:#df382c}label.has-warning{color:#eca918}label.has-success{color:#38b44a}label.has-information{color:#19b6ee}input[type='reset']{display:none}input[type='search']{-webkit-appearance:none;border-radius:0}input[type='checkbox']+label,input[type='radio']+label{display:inline;margin-left:5px;vertical-align:middle;width:auto}input.has-error{border:1px solid #df382c}input.has-warning{border:1px solid #eca918}input.has-success{border:1px solid #38b44a}input.has-information{border:1px solid #19b6ee}form ul{margin-left:0;list-style:none}textarea{overflow:auto;vertical-align:top}textarea[readonly='readonly']{color:#888;cursor:default}textarea[readonly='readonly']:active,textarea[readonly='readonly']:focus{border-color:#888;outline:none}fieldset{background-color:#f7f7f7;background-position:-15px -15px;background-repeat:no-repeat;border-radius:4px;border:0;margin-bottom:8px;padding:15px 20px}@media only screen and (min-width: 1440px){fieldset{padding:15px 20px}}fieldset h3{border-bottom:1px dotted #cdcdcd;margin-bottom:9px;padding-bottom:10px}form input,form select,form textarea{width:100%}@media only screen and (min-width: 768px){.one-col{float:left;width:6.40351%;margin-right:2.10526%}.one-col .one-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.two-col{float:left;width:14.91228%;margin-right:2.10526%}.two-col .one-col{float:left;width:43.81443%;margin-right:12.37113%}.two-col .two-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.three-col{float:left;width:23.42105%;margin-right:2.10526%}.three-col .one-col{float:left;width:27.83505%;margin-right:8.24742%}.three-col .two-col{float:left;width:63.91753%;margin-right:8.24742%}.three-col .three-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.four-col{float:left;width:31.92982%;margin-right:2.10526%}.four-col .one-col{float:left;width:20.36082%;margin-right:6.18557%}.four-col .two-col{float:left;width:46.90722%;margin-right:6.18557%}.four-col .three-col{float:left;width:73.45361%;margin-right:6.18557%}.four-col .four-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.five-col{float:left;width:40.4386%;margin-right:2.10526%}.five-col .one-col{float:left;width:16.04124%;margin-right:4.94845%}.five-col .two-col{float:left;width:37.03093%;margin-right:4.94845%}.five-col .three-col{float:left;width:58.02062%;margin-right:4.94845%}.five-col .four-col{float:left;width:79.01031%;margin-right:4.94845%}.five-col .five-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.six-col{float:left;width:48.94737%;margin-right:2.10526%}.six-col .one-col{float:left;width:13.23024%;margin-right:4.12371%}.six-col .two-col{float:left;width:30.58419%;margin-right:4.12371%}.six-col .three-col{float:left;width:47.93814%;margin-right:4.12371%}.six-col .four-col{float:left;width:65.2921%;margin-right:4.12371%}.six-col .five-col{float:left;width:82.64605%;margin-right:4.12371%}.six-col .six-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.seven-col{float:left;width:57.45614%;margin-right:2.10526%}.seven-col .one-col{float:left;width:11.25605%;margin-right:3.53461%}.seven-col .two-col{float:left;width:26.04671%;margin-right:3.53461%}.seven-col .three-col{float:left;width:40.83737%;margin-right:3.53461%}.seven-col .four-col{float:left;width:55.62802%;margin-right:3.53461%}.seven-col .five-col{float:left;width:70.41868%;margin-right:3.53461%}.seven-col .six-col{float:left;width:85.20934%;margin-right:3.53461%}.seven-col .seven-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.eight-col{float:left;width:65.96491%;margin-right:2.10526%}.eight-col .one-col{float:left;width:9.79381%;margin-right:3.09278%}.eight-col .two-col{float:left;width:22.68041%;margin-right:3.09278%}.eight-col .three-col{float:left;width:35.56701%;margin-right:3.09278%}.eight-col .four-col{float:left;width:48.45361%;margin-right:3.09278%}.eight-col .five-col{float:left;width:61.34021%;margin-right:3.09278%}.eight-col .six-col{float:left;width:74.2268%;margin-right:3.09278%}.eight-col .seven-col{float:left;width:87.1134%;margin-right:3.09278%}.eight-col .eight-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.nine-col{float:left;width:74.47368%;margin-right:2.10526%}.nine-col .one-col{float:left;width:8.66743%;margin-right:2.74914%}.nine-col .two-col{float:left;width:20.084%;margin-right:2.74914%}.nine-col .three-col{float:left;width:31.50057%;margin-right:2.74914%}.nine-col .four-col{float:left;width:42.91714%;margin-right:2.74914%}.nine-col .five-col{float:left;width:54.33372%;margin-right:2.74914%}.nine-col .six-col{float:left;width:65.75029%;margin-right:2.74914%}.nine-col .seven-col{float:left;width:77.16686%;margin-right:2.74914%}.nine-col .eight-col{float:left;width:88.58343%;margin-right:2.74914%}.nine-col .nine-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.ten-col{float:left;width:82.98246%;margin-right:2.10526%}.ten-col .one-col{float:left;width:7.7732%;margin-right:2.47423%}.ten-col .two-col{float:left;width:18.02062%;margin-right:2.47423%}.ten-col .three-col{float:left;width:28.26804%;margin-right:2.47423%}.ten-col .four-col{float:left;width:38.51546%;margin-right:2.47423%}.ten-col .five-col{float:left;width:48.76289%;margin-right:2.47423%}.ten-col .six-col{float:left;width:59.01031%;margin-right:2.47423%}.ten-col .seven-col{float:left;width:69.25773%;margin-right:2.47423%}.ten-col .eight-col{float:left;width:79.50515%;margin-right:2.47423%}.ten-col .nine-col{float:left;width:89.75258%;margin-right:2.47423%}.ten-col .ten-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.eleven-col{float:left;width:91.49123%;margin-right:2.10526%}.eleven-col .one-col{float:left;width:7.04609%;margin-right:2.2493%}.eleven-col .two-col{float:left;width:16.34148%;margin-right:2.2493%}.eleven-col .three-col{float:left;width:25.63687%;margin-right:2.2493%}.eleven-col .four-col{float:left;width:34.93227%;margin-right:2.2493%}.eleven-col .five-col{float:left;width:44.22766%;margin-right:2.2493%}.eleven-col .six-col{float:left;width:53.52305%;margin-right:2.2493%}.eleven-col .seven-col{float:left;width:62.81844%;margin-right:2.2493%}.eleven-col .eight-col{float:left;width:72.11383%;margin-right:2.2493%}.eleven-col .nine-col{float:left;width:81.40922%;margin-right:2.2493%}.eleven-col .ten-col{float:left;width:90.70461%;margin-right:2.2493%}.eleven-col .eleven-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.prepend-one{margin-left:8.50877%}.prepend-two{margin-left:17.01754%}.prepend-three{margin-left:25.52632%}.prepend-four{margin-left:34.03509%}.prepend-five{margin-left:42.54386%}.prepend-six{margin-left:51.05263%}.prepend-seven{margin-left:59.5614%}.prepend-eight{margin-left:68.07018%}.prepend-nine{margin-left:76.57895%}.prepend-ten{margin-left:85.08772%}.prepend-eleven{margin-left:93.59649%}}@media only screen and (min-width: 768px){.append-one{margin-right:8.50877%}.append-two{margin-right:17.01754%}.append-three{margin-right:25.52632%}.append-four{margin-right:34.03509%}.append-five{margin-right:42.54386%}.append-six{margin-right:51.05263%}.append-seven{margin-right:59.5614%}.append-eight{margin-right:68.07018%}.append-nine{margin-right:76.57895%}.append-ten{margin-right:85.08772%}.append-eleven{margin-right:93.59649%}}@media only screen and (min-width: 768px){.push-one{float:left;position:relative;margin:0 -8.50877% 0 8.50877%}.push-two{float:left;position:relative;margin:0 -19.12281% 0 19.12281%}.push-three{float:left;position:relative;margin:0 -27.63158% 0 27.63158%}.push-four{float:left;position:relative;margin:0 -36.14035% 0 36.14035%}.push-five{float:left;position:relative;margin:0 -44.64912% 0 44.64912%}.push-six{float:left;position:relative;margin:0 -53.15789% 0 53.15789%}.push-seven{float:left;position:relative;margin:0 -61.66667% 0 61.66667%}.push-eight{float:left;position:relative;margin:0 -70.17544% 0 70.17544%}.push-nine{float:left;position:relative;margin:0 -78.68421% 0 78.68421%}.push-ten{float:left;position:relative;margin:0 -87.19298% 0 87.19298%}.push-eleven{float:left;position:relative;margin:0 -95.70175% 0 95.70175%}}@media only screen and (min-width: 768px){.pull-one{float:left;position:relative;margin-left:-6.40351%}.pull-two{float:left;position:relative;margin-left:17.01754%}.pull-three{float:left;position:relative;margin-left:25.52632%}.pull-four{float:left;position:relative;margin-left:34.03509%}.pull-five{float:left;position:relative;margin-left:42.54386%}.pull-six{float:left;position:relative;margin-left:51.05263%}.pull-seven{float:left;position:relative;margin-left:59.5614%}.pull-eight{float:left;position:relative;margin-left:68.07018%}.pull-nine{float:left;position:relative;margin-left:76.57895%}.pull-ten{float:left;position:relative;margin-left:85.08772%}.pull-eleven{float:left;position:relative;margin-left:93.59649%}}.banner{background:#f7f7f7;border-bottom:0;border-top:0;box-shadow:inset 0 -1px 0 #ccc;display:block;min-width:100%;position:relative;width:auto;z-index:2}@media only screen and (min-width: 620px){.banner{border-bottom:1px solid #ccc;box-shadow:none}}.banner .logo{position:relative;display:table;float:left;overflow:hidden;height:48px;margin:0 10px;border-left:0}@media only screen and (min-width: 620px){.banner .logo{margin:0 15px}}.banner .logo a{display:table-cell;height:100%;vertical-align:middle;color:#fff}.banner .logo a span{display:inline-block}.banner h2{font-size:1.563em;position:relative;top:14px;left:4px;display:block;margin-bottom:0;text-transform:lowercase}.banner h2 a,.banner h2 a:link,.banner h2 a:visited{float:left;text-decoration:none;color:#fff}.banner .nav-primary{overflow:hidden;margin:0 auto;border:0}@media only screen and (min-width: 620px){.banner .nav-primary{max-width:1440px}}.banner .nav-primary span{display:none}.banner .nav-primary ul{display:none;float:left;width:100%;margin:0;padding:0;border-top:1px solid #ccc;box-shadow:inset 0 -1px 0 #ccc;background-color:#ebebeb}@media only screen and (min-width: 620px){.banner .nav-primary ul{position:static;display:block;width:auto;border-top:0;background-color:transparent;box-shadow:none}}.banner .nav-primary ul li{float:left;width:50%;margin:0;border-right:1px solid #ccc;border-bottom:1px solid #ccc;background:transparent}@media only screen and (min-width: 620px){.banner .nav-primary ul li{width:auto;border-right:0;border-bottom:0;border-left:1px solid #e3e3e3}.banner .nav-primary ul li:last-child{border-right:1px solid #e3e3e3}.banner .nav-primary ul li:last-child a{border-right:0}}@media only screen and (max-width: 620px){.banner .nav-primary ul li:nth-child(2n+2){border-right:0}}.banner .nav-primary ul li a,.banner .nav-primary ul li a:link,.banner .nav-primary ul li a:visited{font-size:1em;font-weight:300;position:relative;display:block;margin-bottom:0;padding:8px 10px;text-align:left;text-decoration:none;background-color:#ebebeb;font-smoothing:subpixel-antialiased;color:#333}@media only screen and (min-width: 620px){.banner .nav-primary ul li a,.banner .nav-primary ul li a:link,.banner .nav-primary ul li a:visited{font-size:.875em;padding:14px 14px 13px;text-align:center;border-left:1px solid #fff;background-color:transparent;color:#fff}}.banner .nav-primary ul li a:hover,.banner .nav-primary ul li a:link:hover,.banner .nav-primary ul li a:visited:hover{background:#f2f2f2}@media only screen and (min-width: 620px){.banner .nav-primary ul li a:hover,.banner .nav-primary ul li a:link:hover,.banner .nav-primary ul li a:visited:hover{background-color:#fff}}.banner .nav-primary ul li a.active,.banner .nav-primary ul li a:link.active,.banner .nav-primary ul li a:visited.active{background-color:#ddd}@media only screen and (min-width: 620px){.banner .nav-primary ul li a.active,.banner .nav-primary ul li a:link.active,.banner .nav-primary ul li a:visited.active{background-color:#e3e3e3;border-left:0}}.banner .nav-toggle{position:absolute;top:0;right:0;display:block;width:48px;height:48px;cursor:pointer;text-indent:-99999px;background-image:url("https://assets.ubuntu.com/v1/12387180-navigation-menu-plain.svg");background-repeat:no-repeat;background-position:center center;background-size:25px auto}@media only screen and (min-width: 620px){.banner .nav-toggle{display:none}}.banner .nav-toggle .nav-toggle__link{width:48px;height:48px}.banner .nav-toggle .nav-toggle__link.open{display:block}.banner .nav-toggle .nav-toggle__link.close{display:none}@media only screen and (max-width: 620px){.banner #nav:hover ul{display:block}.banner #nav:hover ul ul{display:none}.banner #nav:hover .nav-toggle .nav-toggle__link.open{display:none}.banner #nav:hover .nav-toggle .nav-toggle__link.close{display:block}}ol,ul{margin-left:20px;margin-bottom:20px}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}nav ul,nav ol{list-style:none;list-style-image:none}li{font-size:1em;line-height:1.5;margin:0 0 .4em;padding:0}.list-ticks li{background-image:url("data:image/svg+xml;utf8, ")}.no-bullets{list-style:none;margin-left:0}.combined-list ul,.combined-list div{margin-bottom:0}.combined-list .last-item{border-bottom:1px dotted #888;padding-bottom:10px}.combined-list .last-col{margin-bottom:20px}.combined-list .last-col .last-item{border-bottom:0;padding-bottom:0}@media only screen and (min-width: 768px){.combined-list ul,.combined-list div{margin-bottom:20px}.combined-list .last-item{border-bottom:0;padding-bottom:0}}.inline-list{margin-left:0}.inline-list li{display:inline;list-style:none;margin-right:20px}.inline-list .last-item{margin-right:0}.list-step{list-style:none;margin-left:60px}@media only screen and (max-width: 1440px){.list-step__item:first-child{margin-top:10px}}@media only screen and (min-width: 1440px){.list-step__title{margin-bottom:0}}.list-step__bullet{box-shadow:0 1px 3px 1px rgba(51,51,51,0.2);background:#fff;border-radius:50%;padding:.3em .68em;display:inline-block;color:#f7f7f7;margin-right:.34375em;margin-bottom:.625em;margin-left:-60px}@media only screen and (max-width: 1440px){.list-step__bullet{position:absolute;top:-5px}}.row{border-bottom:1px dotted #888;clear:both;padding:20px 10px 0;position:relative}.row br{display:none}.row.no-padding-bottom{padding-bottom:0}.row::after{clear:both;content:'.';display:block;height:0;visibility:hidden}.row--light{background:#fff}.row--dark{background:#333;color:#fff}.row.row-grey,.row.row--grey{background:#f7f7f7;border:0;margin-top:-1px}.no-border{border:0}.row-hero{margin-top:20px;padding-top:0}.strip{width:100%;display:block}.strip-inner-wrapper{max-width:1440px;margin:auto}@media only screen and (min-width: 768px){.row-hero{margin-top:40px}.row{border-radius:0;margin:0;padding:40px 20px 20px}.row-grey{margin-top:-1px}}@media only screen and (min-width: 769px){.row br{display:block}}@media only screen and (min-width: 1440px){.row br{display:block}.row{padding:60px 20px 40px}.no-border{border:0}}.header-search [type="search"],.header-search [type="text"],.search-form [type="search"],.search-form [type="text"]{-webkit-appearance:none;background-color:#dedede;box-shadow:none;color:#333;display:block;float:left;font-size:1em;margin-bottom:0;padding:12px 10px;transition:all .5s ease-out;width:100%}.header-search .svg-search-handle,.search-form .svg-search-handle{fill:#333}.header-search .svg-search-frame,.search-form .svg-search-frame{stroke:#333}.header-search placeholder,.search-form placeholder{color:#333;opacity:1}.header-search input:placeholder,.search-form input:placeholder{color:#333;opacity:1}.header-search ::placeholder,.search-form ::placeholder{color:#333;opacity:1}.header-search [type="search"]:focus,.search-form [type="search"]:focus{background-color:#d3d3d3;border-color:#b3b3b3}.header-search [type=submit],.search-form [type=submit]{background:none;display:block;float:left;line-height:0;margin-left:-40px;overflow:visible;padding:3px 2px;width:auto}.header-search [type=submit]:hover,.search-form [type=submit]:hover{background:none}.header-search [type=submit] img,.search-form [type=submit] img{height:28px;width:28px;margin-top:2px}.banner .search-toggle{background-image:url("data:image/svg+xml;utf8, ");background-position:center center;background-repeat:no-repeat;background-size:20px 20px;display:block;height:48px;outline:none;overflow:hidden;position:absolute;right:0;text-indent:-999em;top:0;width:24px}.banner .search-toggle .search-toggle__link{width:48px;height:48px}.banner .search-toggle .search-toggle__link.open{display:block}.banner .search-toggle .search-toggle__link.close{display:none}#site-search:hover form{display:block}#site-search:hover .search-toggle .search-toggle__link.open{display:none}#site-search:hover .search-toggle .search-toggle__link.close{display:block}.header-search,.search-form{background:#f7f7f7;border:0;display:none;float:left;position:relative;margin:0;width:100%;z-index:3}.search-form.active,.header-search.active,.header-search.open{display:block}.search-form div,.header-search div{box-shadow:inset 0 -4px 4px -4px rgba(0,0,0,0.3),inset 0 5px 5px -5px rgba(0,0,0,0.3);background:#fff;margin:10px;position:relative;z-index:1}.search-form form [type="search"],.header-search form [type="search"]{background:#fff;border:0;box-shadow:0 2px 2px rgba(0,0,0,0.3) inset,0 -1px 3px rgba(0,0,0,0.2) inset,0 2px 0 rgba(255,255,255,0.4);color:#333;display:block;float:left;font-size:1em;height:auto;margin:0;padding:8px 10px;width:100%}.yes-js .header-inner .search-form,.yes-js .header-inner .header-search{display:none}.yes-js .header-inner .search-form form,.yes-js .header-inner .header-search form{margin-left:0;margin-right:0;overflow:hidden;padding:10px;position:relative;top:0;width:100%;z-index:999}@media only screen and (max-width: 620px){.banner .search-toggle{right:0}.no-svg .search-toggle,.opera-mini .search-toggle{background-image:url("https://assets.ubuntu.com/v1/75d8151d-search-white.png")}}@media only screen and (min-width: 620px){.banner .search-toggle{display:none}}@media only screen and (min-width: 620px){.search-form,.header-search{background:none;border-right:0 none;float:right;margin-bottom:0;max-width:220px;overflow:hidden;padding:7px 0 5px 14px}.search-form form [type="text"],.search-form form [type="search"],.header-search form [type="text"],.header-search form [type="search"]{box-shadow:0 2px 4px rgba(0,0,0,0.4) inset;box-sizing:content-box;background-color:#dedede;border:0 solid #d8d8d8;border-width:0 0 1px;color:#333;font-size:.813em;height:24px;margin-bottom:0;padding:.5em 2.5em .5em .5em;transition:all .5s ease 0s;width:86px}.search-form form [type="text"]:focus,.search-form form [type="search"]:focus,.header-search form [type="text"]:focus,.header-search form [type="search"]:focus{background-color:#d3d3d3;border-color:#c4c4c4}.search-form .svg-search-handle,.header-search .svg-search-handle{fill:#333}.search-form .svg-search-frame,.header-search .svg-search-frame{stroke:#333}.search-form placeholder,.header-search placeholder{color:#333}.search-form input:placeholder,.header-search input:placeholder{color:#333}.search-form ::placeholder,.header-search ::placeholder{color:#333}}@media only screen and (min-width: 620px){.header-search .svg-search-handle,.search-form .svg-search-handle{fill:#333}.header-search .svg-search-frame,.search-form .svg-search-frame{stroke:#333}.header-search placeholder,.search-form placeholder{color:#333}.header-search input:placeholder,.search-form input:placeholder{color:#333}.header-search ::placeholder,.search-form ::placeholder{color:#333}}@media only screen and (max-width: 620px){.banner .nav-primary .header-search{position:relative;top:0;width:100%}.banner .nav-primary .header-search [type="search"]{background-color:#ebebeb;color:#333}.banner .nav-primary .header-search [type="submit"]{background-color:transparent;height:38px;margin-top:0;width:32px}.banner .nav-primary .header-search [type="submit"] img,.banner .nav-primary .header-search [type="submit"] svg{max-width:none}.banner .nav-primary .header-search.open{display:block}.banner .search-toggle{background-image:url("data:image/svg+xml;utf8, ");background-position:center center;background-repeat:no-repeat;background-size:25px auto;cursor:pointer;display:block;height:48px;position:absolute;right:0;text-indent:-99999px;width:48px}.no-svg .banner .search-toggle,.opera-mini .banner .search-toggle{background-image:url("https://assets.ubuntu.com/v1/2196e362-search-white.svg")}.opera-mini x:-o-prefocus,.opera-mini .banner .search-toggle{background-size:25px auto}}@media only screen and (min-width: 620px){.search-form,.header-search{display:block}.search-form .svg-search-handle,.header-search .svg-search-handle{fill:#333}.search-form .svg-search-frame,.header-search .svg-search-frame{stroke:#333}.search-form form [type="text"]:focus,.header-search form [type="text"]:focus{width:160px}.search-form [type="search"],.search-form [type="text"],.header-search [type="search"],.header-search [type="text"]{padding:8px 10px}}@media only screen and (max-width: 1440px){.search-form,.header-search{margin-right:10px}}@media only screen and (max-width: 620px){.banner .search-toggle{right:48px}}.ubuntu-search .nav-secondary,.search-results .nav-secondary,.search-no-results .nav-secondary{display:none}.ubuntu-search section>h1,.ubuntu-search section article h1,.search-results section>h1,.search-results section article h1,.search-no-results section>h1,.search-no-results section article h1{padding-bottom:10px;font-size:1.438em;margin-bottom:0}.ubuntu-search section>h1,.search-results section>h1,.search-no-results section>h1{border-bottom:1px dotted #cdcdcd}.ubuntu-search .main-search,.search-results .main-search,.search-no-results .main-search{background-color:transparent;margin:0 0 20px;padding:20px 0}.ubuntu-search .main-search [type="search"],.search-results .main-search [type="search"],.search-no-results .main-search [type="search"]{border:1px solid #888;float:left;font-size:2em;padding:.2em 65px .2em .2em;width:100%}.ubuntu-search .main-search [type=submit],.search-results .main-search [type=submit],.search-no-results .main-search [type=submit]{width:32px;height:38px;background-repeat:no-repeat;background-position:center 8px;background-color:transparent;background-size:28px 28px;display:block;float:left;line-height:0;margin-left:-53px;margin-top:-4px;overflow:visible;padding:4px}.ubuntu-search .main-search [type=submit] img,.search-results .main-search [type=submit] img,.search-no-results .main-search [type=submit] img{height:45px;width:45px}.ubuntu-search .main-search [type=submit]:hover,.search-results .main-search [type=submit]:hover,.search-no-results .main-search [type=submit]:hover{background:none}.ubuntu-search .search-result h1 .title-main,.search-results .search-result h1 .title-main,.search-no-results .search-result h1 .title-main{margin-right:20px}.ubuntu-search .search-result h1 .result-url,.search-results .search-result h1 .result-url,.search-no-results .search-result h1 .result-url{color:#888;display:block;overflow:hidden;padding-bottom:2px;text-overflow:ellipsis;vertical-align:bottom}.ubuntu-search .search-result h1 .result-url a,.search-results .search-result h1 .result-url a,.search-no-results .search-result h1 .result-url a{color:#888}.ubuntu-search .search-result p,.search-results .search-result p,.search-no-results .search-result p{margin-bottom:0}.ubuntu-search .num-results,.search-results .num-results,.search-no-results .num-results{display:inline-block;margin-left:20px}.ubuntu-search .bottom-results-total,.search-results .bottom-results-total,.search-no-results .bottom-results-total{margin:0;overflow:visible;padding-top:20px;text-align:center;width:100%}.ubuntu-search .bottom-nav,.search-results .bottom-nav,.search-no-results .bottom-nav{margin-top:-26px;overflow:hidden}.ubuntu-search .bottom-nav ul,.search-results .bottom-nav ul,.search-no-results .bottom-nav ul{margin-bottom:0;margin-left:0;overflow:hidden;padding:0}.ubuntu-search .bottom-nav li,.search-results .bottom-nav li,.search-no-results .bottom-nav li{float:left;margin-left:15px}.ubuntu-search .bottom-nav li:first-child,.search-results .bottom-nav li:first-child,.search-no-results .bottom-nav li:first-child{margin-left:0}.ubuntu-search .nav-back,.search-results .nav-back,.search-no-results .nav-back{float:left}.ubuntu-search .nav-back li:before,.search-results .nav-back li:before,.search-no-results .nav-back li:before{color:#f7f7f7;content:'\2039';margin-right:5px}.ubuntu-search .nav-back .item-extreme:before,.search-results .nav-back .item-extreme:before,.search-no-results .nav-back .item-extreme:before{content:'\2039\2039'}.ubuntu-search .nav-forward,.search-results .nav-forward,.search-no-results .nav-forward{float:right}.ubuntu-search .nav-forward li:after,.search-results .nav-forward li:after,.search-no-results .nav-forward li:after{color:#f7f7f7;content:'\203A';margin-left:5px}.ubuntu-search .nav-forward .item-extreme:after,.search-results .nav-forward .item-extreme:after,.search-no-results .nav-forward .item-extreme:after{content:'\203A\203A'}.ubuntu-search .error-notification,.search-results .error-notification,.search-no-results .error-notification{background-color:#fff;color:#333;display:block;margin-top:20px;padding:20px;width:100%}.ubuntu-search .result-line,.search-results .result-line,.search-no-results .result-line{color:#888}.ubuntu-search .results-top,.search-results .results-top,.search-no-results .results-top{border-bottom:1px dotted #cdcdcd;padding-bottom:.5em}.ubuntu-search .search-container,.search-results .search-container,.search-no-results .search-container{padding-bottom:0}@media only screen and (min-width: 620px){.ubuntu-search .main-search [type=submit]{margin-left:-60px;margin-top:0}}@font-face{font-family:'Ubuntu';font-style:normal;font-weight:300;src:url("../assets/fonts/ubuntu-l-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-l-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:normal;font-weight:400;src:url("../assets/fonts/ubuntu-r-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-r-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:normal;font-weight:700;src:url("../assets/fonts/ubuntu-b-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-b-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:italic;font-weight:300;src:url("../assets/fonts/ubuntu-li-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-li-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:italic;font-weight:400;src:url("../assets/fonts/ubuntu-ri-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-ri-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:italic;font-weight:700;src:url("../assets/fonts/ubuntu-bi-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-bi-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu Mono';font-style:normal;font-weight:400;src:url("../assets/fonts/ubuntumono-r-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntumono-r-webfont.woff") format("woff")}body{color:#333;font-family:'Ubuntu', Arial, 'libra sans', sans-serif;font-size:16px;font-weight:300}a:focus{outline:thin dotted}a:hover,a:active{outline:0}a{color:#f7f7f7;text-decoration:none}a:hover,a:active,a:focus{text-decoration:underline}strong{font-weight:400}.caps-centered,.muted-heading{font-size:.875em;margin-bottom:20px;text-align:center;text-transform:uppercase}small,.smaller{font-size:13px}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{vertical-align:text-top}sub{vertical-align:text-bottom}pre{border-radius:4px;background-color:#fff;padding:.6em 1em;white-space:pre-wrap;word-wrap:break-word}p+h2,ul+h2,ol+h2,pre+h2{margin-top:0.5625em}header nav a:link{font-weight:normal}p+h3,ul+h3,ol+h3,pre+h3{margin-top:.783em}p+h4,ul+h4,ol+h4,pre+h4{margin-top:1.21875}ol+h2,p+h2,pre+h2,ul+h2{margin-top:.563em}ol+h3,p+h3,pre+h3,ul+h3{margin-top:.783em}ol+h4,p+h4,pre+h4,ul+h4{margin-top:1.219em}.intro{font-size:1em;line-height:1.4}.row div p:last-child,.row ul p:last-child{margin-bottom:0}.four-col p:last-child{margin-bottom:0}.note{color:#888;font-size:.813em}h1,h2,h3,h4,h5,h6{font-family:Ubuntu, Arial, 'libra sans', sans-serif;font-weight:300;line-height:1.3}h1{font-size:2.8125em;margin-bottom:.5em}h2{font-size:2em;margin-bottom:.5em}h3{font-size:1.4375em;margin-bottom:.522em}h4{font-size:1.25em;font-weight:400;margin-bottom:.615em}h5{font-size:1em;font-weight:700;margin-bottom:1em}h6{font-size:.8125em;font-weight:400;margin-bottom:1em;letter-spacing:.1em;text-transform:uppercase}p,li{font-size:1em;line-height:1.5;margin:0;margin-bottom:.75em;padding:0}button,input,select,textarea{font-family:Ubuntu,Arial,'libra sans',sans-serif}@media only screen and (min-width: 768px) and (max-width: 1440px){h1{font-size:1.5234375em;margin-bottom:.5em}h2{font-size:1.348125em;margin-bottom:.5em}h3{font-size:1.1428125em;margin-bottom:.522em}h4{font-size:1.171875em;font-weight:400;margin-bottom:.615em}h5{font-size:.9375em;font-weight:700;margin-bottom:1em}h6{font-size:.6778125em;font-weight:400;margin-bottom:1em;letter-spacing:.1em;text-transform:uppercase}.intro{font-size:1.13333em}}@media only screen and (max-width: 768px){h1{font-size:1.421875em;margin-bottom:.4375em}h2{font-size:1.25825em;margin-bottom:.4375em}h3{font-size:1.066625em;margin-bottom:.45675em}h4{font-size:1.09375em;font-weight:400;margin-bottom:.538125em}h5{font-size:.875em;font-weight:700;margin-bottom:.875em}h6{font-size:.632625em;font-weight:400;margin-bottom:.875em;letter-spacing:.1em;text-transform:uppercase}p,li{font-size:.875em}}@media only screen and (min-width: 1440px){p,li,code,pre{font-size:16px;line-height:1.5;margin-bottom:.75em}.intro{font-size:1.25em}}dfn{font-style:italic}code,pre{font-family:'Ubuntu Mono', 'Consolas', 'Monaco', 'Lucida Console', 'Courier New', Courier, monospace}table{border-collapse:collapse;border-spacing:0;overflow-x:scroll;margin-bottom:20px;margin:0 0 2.5em;width:100%}table th,table td{background:#f7f7f7;border:1px dotted #888;padding:15px 10px}table td{text-align:center;vertical-align:middle}table thead th{border-collapse:separate;border-spacing:0 10px;background:#fee3d2;color:#333;font-weight:normal}table tbody th{font-weight:300;text-align:left}table th[scope='col']{text-align:center}table thead th:first-of-type{text-align:left}@media only screen and (max-width: 768px){table{display:block}}body footer.global #nav-global li:first-of-type a{margin-left:0}footer.global{background:none;border-top:0;box-shadow:inset 0 2px 2px -1px #cdcdcd;clear:both;display:block;padding:30px 10px 20px;position:relative;width:100%}footer.global .legal{background-image:none;clear:both;margin:0 auto;min-height:40px;position:relative;width:100%}footer.global .legal p,footer.global .legal ul{padding-left:0}footer.global h2{font-size:.75em;line-height:1.4;margin-bottom:0;padding-bottom:.5em}footer.global h2,footer.global h2 a:link,footer.global h2 a:visited{color:#333;font-weight:normal}footer.global ul{margin:0}footer.global li a:hover{color:#f7f7f7}footer.global li ul li a:link,footer.global li ul li a:visited{color:#333;font-size:.75em}footer.global h2 a:hover,footer.global h2 a:active{color:#f7f7f7}footer.global p{color:#333;font-size:12px;margin-bottom:0}@media only screen and (max-width: 768px){footer.no-global .legal{box-shadow:0 2px 2px -1px #cdcdcd inset;box-sizing:content-box;margin-left:-10px;padding-left:10px;padding-right:10px;padding-top:10px}}@media only screen and (min-width: 1440px){footer.global .legal{width:1440px}footer.global{padding:30px 0 20px}}svg:not(:root){overflow:hidden}figure{width:100%}figure caption{display:block;width:100%;text-align:center}object,iframe,embed,canvas,video,audio{display:block;max-width:100%;margin:0 auto 20px}audio:not([controls]){display:none;height:0}[hidden]{display:none}.video-container{position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden}.video-container iframe{position:absolute;top:0;left:0;width:100%;height:100%}.video-container h3,.video-container .video-title{margin-top:20px}.inline-logos{float:left;margin-left:0;padding:0;text-align:center;width:100%}.inline-logos .inline-logos__item{display:inline-block;margin-right:20px;padding:0 0 20px;width:36%}@media only screen and (max-width: 300px){.inline-logos .inline-logos__item{margin:0 0 20px;width:100%}}.inline-logos .inline-logos__item.clear-row{clear:left}.inline-logos .inline-logos__item.last-item{border:0}.inline-logos .inline-logos__image{max-height:32px;max-width:115px;transition:all .3s ease-out;vertical-align:middle}@media only screen and (min-width: 768px){.inline-logos{padding-top:20px}.inline-logos .inline-logos__item{display:inline-block;height:56px;line-height:60px;margin:0 30px 30px;width:150px}.inline-logos .inline-logos__image{float:none;height:auto;max-height:56px;max-width:150px;vertical-align:middle}}@media only screen and (min-width: 1440px){.inline-logos__item{margin-bottom:40px}}@media only screen and (min-width: 768px){.equal-height__align-vertically{align-items:center;justify-content:center}}@media only screen and (min-width: 768px){.equal-height--vertical-divider{position:relative}.equal-height--vertical-divider__item{padding-left:10px;padding-right:10px}.equal-height--vertical-divider__item:before{content:'';position:absolute;right:-10px;top:0;height:100%;width:1px;border-right:1px dotted #888}.equal-height--vertical-divider .last-col,.equal-height--vertical-divider__item:last-of-type{padding-right:0}.equal-height--vertical-divider .last-col:before,.equal-height--vertical-divider__item:last-of-type:before{border-right:0}.equal-height--vertical-divider__item:first-of-type{padding-left:0}}@media only screen and (max-width: 768px){.equal-height--vertical-divider .equal-height--vertical-divider__item{border-bottom:1px dotted #888;padding-bottom:20px}.equal-height--vertical-divider .equal-height--vertical-divider__item:last-of-type{border-bottom:0;padding-bottom:inherit}}body{background:url("../assets/img/backgrounds/image-background-paper.png") repeat-y center top #f7f7f7}.button--neutral,.button--inline-neutral{color:#333;background-color:transparent;border:1px solid #cdcdcd}.button--neutral:focus,.button--neutral:active,.button--neutral:hover,.button--inline-neutral:focus,.button--inline-neutral:active,.button--inline-neutral:hover{background-color:rgba(0,0,0,0.1)}.button--neutral:disabled:focus,.button--neutral:disabled:active,.button--neutral:disabled:hover,.button--neutral.button--disabled:focus,.button--neutral.button--disabled:active,.button--neutral.button--disabled:hover,.button--inline-neutral:disabled:focus,.button--inline-neutral:disabled:active,.button--inline-neutral:disabled:hover,.button--inline-neutral.button--disabled:focus,.button--inline-neutral.button--disabled:active,.button--inline-neutral.button--disabled:hover{background-color:transparent}.button--inline-neutral{display:inline-block;width:auto}.button--positive,.button--inline-positive{color:#fff;background-color:#38b44a}.button--positive:focus,.button--positive:active,.button--positive:hover,.button--inline-positive:focus,.button--inline-positive:active,.button--inline-positive:hover{background-color:#2c8d3a}.button--positive:disabled:focus,.button--positive:disabled:active,.button--positive:disabled:hover,.button--positive.button--disabled:focus,.button--positive.button--disabled:active,.button--positive.button--disabled:hover,.button--inline-positive:disabled:focus,.button--inline-positive:disabled:active,.button--inline-positive:disabled:hover,.button--inline-positive.button--disabled:focus,.button--inline-positive.button--disabled:active,.button--inline-positive.button--disabled:hover{background-color:#38b44a}.button--inline-positive{display:inline-block;width:auto}.button--destructive,.button--inline-destructive{color:#fff;background-color:#df382c}.button--destructive:focus,.button--destructive:active,.button--destructive:hover,.button--inline-destructive:focus,.button--inline-destructive:active,.button--inline-destructive:hover{background-color:#bc271c}.button--destructive:disabled:focus,.button--destructive:disabled:active,.button--destructive:disabled:hover,.button--destructive.button--disabled:focus,.button--destructive.button--disabled:active,.button--destructive.button--disabled:hover,.button--inline-destructive:disabled:focus,.button--inline-destructive:disabled:active,.button--inline-destructive:disabled:hover,.button--inline-destructive.button--disabled:focus,.button--inline-destructive.button--disabled:active,.button--inline-destructive.button--disabled:hover{background-color:#df382c}.button--inline-destructive{display:inline-block;width:auto}.button--base,.button--inline-base{color:#333;background-color:transparent}.button--base:focus,.button--base:active,.button--base:hover,.button--inline-base:focus,.button--inline-base:active,.button--inline-base:hover{background-color:rgba(0,0,0,0.1)}.button--base:disabled:focus,.button--base:disabled:active,.button--base:disabled:hover,.button--base.button--disabled:focus,.button--base.button--disabled:active,.button--base.button--disabled:hover,.button--inline-base:disabled:focus,.button--inline-base:disabled:active,.button--inline-base:disabled:hover,.button--inline-base.button--disabled:focus,.button--inline-base.button--disabled:active,.button--inline-base.button--disabled:hover{background-color:transparent}.button--inline-base{display:inline-block;width:auto}.cookie-policy{box-shadow:0 -1px 2px rgba(0,0,0,0.2);background-color:#fae4dc;position:fixed;bottom:0;width:100%;z-index:100}.cookie-policy p{font-size:.8125em;margin-bottom:0;margin-left:0;padding:8px 0;width:100%}.cookie-policy .link-cta{background-image:url("https://assets.ubuntu.com/v1/3f057022-close-orange.svg");background-repeat:no-repeat;color:#fff;float:right;font-size:1em;height:15px;margin:12px 0;margin-top:12px;padding:0;text-decoration:none;text-indent:-9999px;width:16px}.no-svg .cookie-policy .link-cta,.opera-mini .cookie-policy .link-cta{background-image:url("https://assets.ubuntu.com/v1/898777ac-close-orange.png")}.deep-link{text-decoration:none}.deep-link:after{content:'';display:inline-block;margin-left:3px;opacity:0;position:relative;top:1px;width:1em;height:1em;background-image:url(https://assets.ubuntu.com/v1/128877a5-anchor_16.svg);background-position:0 80%;background-repeat:no-repeat;background-size:16px;transition:opacity .1s}.deep-link:hover{text-decoration:none}.deep-link:hover:after{opacity:1}.banner{background:#000;box-shadow:none}@media only screen and (min-width: 620px){.banner .nav-primary{width:auto}.banner ul{border-top:0;border-right:0;box-shadow:none}.banner li{width:auto;border-right:0;border-bottom:0}.banner li:last-child a{border-right:0}.banner a,.banner a:link,.banner a:visited{border-left:0}}@media only screen and (max-width: 768px){footer.global{padding-top:0}}.instruction{padding:0;position:relative;width:100%;border-bottom:1px dotted #ddd}.instruction .instruction__bullet,.instruction .instruction__details{padding:60px 40px;margin-bottom:0}.instruction .instruction__bullet{margin-right:0}.instruction .instruction__step{display:inline-block;border-radius:50%;width:50px;height:50px;line-height:50px;color:#fff;text-align:center;vertical-align:top;font-size:2em;font-weight:400}.instruction .instruction__title{display:inline;position:relative;top:6px;line-height:1.6}.instruction .command-line{position:relative;border-radius:4px;background-color:#2c001e;border:1px solid #2c001e;overflow:hidden;transition:all .2s}.instruction .command-line .command-line__input{border:0;background:transparent;font-size:1em;font-family:Ubuntu Mono;font-weight:300;padding:.7em 1em;color:#fff;width:100%}.instruction .command-line .command-line__copy-button{transition:background-color .2s;position:absolute;right:0;top:0;width:50px;border:0;height:100%;display:block;text-indent:-9999px;background-image:url("https://assets.ubuntu.com/v1/994e60f9-get-link-url_16.svg");background-repeat:no-repeat;background-position:center;background-color:#fff}.row{border-bottom:0;background-color:rgba(255,255,255,0.6)}@media only screen and (max-width: 768px){.row{padding-bottom:20px}}@media only screen and (max-width: 768px){.row-hero{padding-top:20px;margin-top:0}}.strip-white{background:#fff}.strip-dark{background:#333;color:#fff}.strip-dark a{color:#fff}.strip-trans{background:transparent}.social-list{list-style:none;padding-left:0;margin-left:0}.social-list__item--twitter,.social-list__item--google-plus,.social-list__item--facebook{margin-right:20px;float:left;text-indent:-99999px;background-image:url("https://assets.ubuntu.com/v1/ed986fc0-icon-social.svg");background-repeat:no-repeat;height:44px;width:44px;overflow:hidden;display:inline-block}.social-list__item--twitter:hover,.social-list__item--google-plus:hover,.social-list__item--facebook:hover{background-position-y:-45px}.social-list__item--twitter{background-position:0 0}.social-list__item--google-plus{background-position:-45px 0}.social-list__item--facebook{background-position:-90px 0}@media only screen and (min-width: 768px){.tooltip{position:relative;display:inline-block}.tooltip__content{position:absolute;z-index:98;left:-1000px;right:-1000px;top:-30px;font-weight:300;margin:auto;display:block;text-align:center;white-space:nowrap}.tooltip:hover .tooltip__content:after{display:table;z-index:98;margin:auto;color:#fff;border-radius:3px;background:#000;box-shadow:none;font-size:12px;content:attr(data-tooltip);padding:4px 6px;white-space:nowrap;text-align:center}.tooltip:hover .tooltip__content:before{position:absolute;top:100%;left:50%;margin-left:-5px;content:'';border:solid transparent;border-width:5px;border-top-color:#000}}a,.link{cursor:pointer;color:#f7f7f7;text-decoration:underline}pre{padding:10px 15px;background-color:#333;border:0;border-radius:2px;color:#cdcdcd}html{overflow-y:scroll;overflow-x:hidden;min-height:100%;color:#333;font:1em / 1.5 "'Ubuntu', Arial, 'libra sans', sans-serif"}a{color:#333;text-decoration:none;border-bottom:1px solid #c2c2c2}a:hover{text-decoration:none;color:#dd4814}p,li,code,pre{font-size:16px;font-size:1rem;line-height:1.5;margin-bottom:.75em}pre,code{text-align:left;white-space:pre-line;word-spacing:normal;word-wrap:break-word;tab-size:4;hyphens:none;direction:ltr}hr{margin-bottom:20px}h1,h2,h3,h4,h5{margin:10px 0;font-weight:300;line-height:1.5;color:#333}h1{font-size:2em}h1 small{font-size:75%}h2{font-size:1.4375em}h2 small{font-size:75%}h3{font-size:1.25em}h3 small{font-size:75%}h4{font-size:1em}h4 small{font-size:75%}h5{font-size:0.875em}h5 small{font-size:75%}.gigantic{font-size:45px;font-size:2.8125rem}.mega{font-size:40px;font-size:2.5rem}input[type='text'],input[type='number'],input[type='search'],input[type='password'],input[type='email'],input[type='url'],input[type='tel']{padding:7px 10px;box-shadow:none;width:100%}input[type='text']::placeholder,input[type='number']::placeholder,input[type='search']::placeholder,input[type='password']::placeholder,input[type='email']::placeholder,input[type='url']::placeholder,input[type='tel']::placeholder{color:#888}input[type='text'][disabled],input[type='number'][disabled],input[type='search'][disabled],input[type='password'][disabled],input[type='email'][disabled],input[type='url'][disabled],input[type='tel'][disabled]{color:#888;background-color:transparent}input[type='number']{padding-right:15px}input[type='search']{appearance:textfield}input[type='search']::-webkit-search-decoration,input[type='search']::-webkit-search-cancel-button{appearance:none}input[type='checkbox']{margin:4px 0 0;margin-top:1px 9;line-height:normal}input[type='radio'],input[type='image']{display:inline-block;margin-right:10px}textarea{height:auto;min-height:175px;padding:7px 10px !important;box-shadow:none;width:100%}textarea::placeholder{color:#888}textarea[disabled]{color:#888;background-color:transparent}select{display:block;clear:both;cursor:pointer;margin:0;background-image:url("../assets/images/forms/chevron-down.svg");background-repeat:no-repeat;background-position:top 14px right 10px;background-color:#fff;padding:7px 30px 7px 10px !important;box-shadow:none;width:100%;-moz-appearance:none;appearance:none;text-indent:.01px;text-overflow:''}select[multiple],select[size]{height:auto;background-image:none;padding-top:10px}select[disabled]{color:#888;background-image:none;background-color:transparent}select:-moz-focusring{color:transparent;text-shadow:0 0 0 #000}select::-ms-expand{display:none}fieldset{background:none;margin-left:0;padding:0}label{display:inline-block;max-width:100%;margin-bottom:5px}[type="checkbox"]:not(:checked),[type="checkbox"]:checked{position:absolute;left:-9999px}[type="checkbox"]+label{height:16px;position:relative;padding-left:25px;cursor:pointer}[type="checkbox"]+label:before{content:'';position:absolute;left:0;top:3px;width:11px;height:11px;border:1px solid #cdcdcd;background:#fff;border-radius:2px}[type="checkbox"]:checked+label:before{background-color:#dd4814;border-color:#dd4814}[type="checkbox"]:checked+label:after{content:'✔';position:absolute;line-height:13px;top:3px;left:3px;font-size:10px;color:#fff;transition:all .2s}@-moz-document url-prefix(){[type="checkbox"]:checked+label:after{font-size:13px;left:1px}}[type="checkbox"][disabled]{cursor:not-allowed}[type="checkbox"][disabled]+label{cursor:not-allowed}[type="checkbox"][disabled]+label:before{opacity:.5}[type="radio"]:not(:checked),[type="radio"]:checked{position:absolute;left:-9999px}[type="radio"]+label{position:relative;padding-left:25px;cursor:pointer}[type="radio"]+label:before{content:'';position:absolute;left:0;top:1px;width:13px;height:13px;border:1px solid #cdcdcd;background:#fff;border-radius:50%}[type="radio"]:checked+label:after{content:'';position:absolute;left:3px;top:4px;width:9px;height:9px;background:#dd4814;border-radius:50%}.tags{width:100%}.tags .tag-list{width:100%;margin:0}.tags .tag-list .tag-item{display:inline-block;float:left;line-height:36px;margin-bottom:0;margin-right:10px;word-wrap:break-word}.tags .tag-list .tag-item .remove-button{border-bottom:0}.tags .input{width:100% !important}.onoffswitch{position:relative;display:inline-block;vertical-align:middle;width:38px;margin:0 0 4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch[disabled] .onoffswitch-checkbox,.onoffswitch[disabled] .onoffswitch-label{opacity:.5;pointer-events:none}.onoffswitch-external-label{display:inline-block;margin-left:5px}.onoffswitch-checkbox{display:none !important}.onoffswitch-label{display:block !important;overflow:hidden;cursor:pointer;border-radius:2px;padding-left:0 !important;margin:0 !important}.onoffswitch-label:before,.onoffswitch-label:after{display:none}.onoffswitch-inner{display:block;width:200%;margin-left:-100%;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:18px;padding:0;line-height:18px;font-size:14px;color:#fff;font-family:Trebuchet, Arial, sans-serif;font-weight:bold;box-sizing:border-box}.onoffswitch-inner:before{content:'';padding-left:10px;background-color:#38b44a;color:#fff}.onoffswitch-inner:after{content:'';padding-right:10px;background-color:#eee;color:#888;text-align:right}.onoffswitch-switch{display:block;width:19px;height:16px;margin:0;background:#fff;position:absolute;top:0;bottom:0;right:19px;border:1px solid #d2d2d2;border-radius:2px;transition:all .3s ease-in 0s;box-sizing:border-box}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-left:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:0}dl dt{clear:left;color:#333}dl dd{color:#333;margin-left:0}dl dt,dl dd{display:inline-block;float:left;line-height:36px;margin-bottom:10px !important;word-wrap:break-word}dl dt:last-child,dl dd:last-child{margin-bottom:0}.wrapper{min-height:100%;height:auto !important;height:100%;margin:0 auto;position:relative;background:rgba(255,255,255,0.4)}.wrapper:after{content:'';position:absolute;display:block;top:0;right:0;bottom:0;left:0;background:url("../assets/images/backgrounds/background-paper.png");height:100%;width:100%;z-index:-1}.wrapper--inner{max-width:1440px;width:100%;margin:0 auto;clear:both;display:block}.row{background-color:transparent;border-top:1px dotted #cdcdcd}.row:first-of-type{border-top:0}.button--secondary{line-height:18px}.button--primary:hover,.button--destructive:hover,.button--positive:hover{color:#fff}.button--base:hover,.button--neutral:hover{color:#333}.button-group{position:relative;width:100%;vertical-align:middle;clear:both;min-width:160px}.button-group.button-group--inline{display:inline-block;width:auto}.button-group .button-group__link{display:inline-block;float:left;text-align:left;padding-right:35px}.button-group .button-group__link::after{content:'';display:inline-block;position:absolute;top:15px;right:15px;width:4px;height:4px;vertical-align:middle;border-right:1px solid;border-bottom:1px solid;transform:rotate(45deg)}.button-group .button-group__dropdown{border-radius:3px;left:0;margin:0;list-style:none;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);z-index:20;max-height:1000px;transition:max-height .3s ease-in;position:absolute;top:36px;clear:both;min-width:160px}.button-group .button-group__item{float:left;clear:both;padding:5px 10px;margin:0;width:100%}.button-group .button-group__item .button-group__item-link{color:#333;cursor:pointer;width:100%;float:left;margin:0;border:0;text-transform:lowercase;white-space:nowrap}.button-group .button-group__item .button-group__item-link:first-letter{text-transform:uppercase}.button-group .button-group__item .button-group__item-link:hover{color:#dd4814;text-decoration:none}.form .form__group{margin-bottom:10px}.form .form__group .form__group-input{position:relative}.form .form__group .form__group-input .form__group-remove{position:absolute;top:10px;right:10px;cursor:pointer;height:16px;width:16px;background:url("../assets/images/icons/remove.svg") no-repeat;background-size:75% 75%;background-position:center;display:inline-block}.form .form__group .form__group-input .icon,.form .form__group .form__group-input .icon--status-failed,.form .form__group .form__group-input .icon--status-in-progress,.form .form__group .form__group-input .icon--status-queued,.form .form__group .form__group-input .icon--status-succeeded,.form .form__group .form__group-input .icon--status-waiting{position:absolute;top:10px;right:10px;display:inline-block}.form .form__group.form__group--subtle .form__group-label{color:#888}.form .form__group.form__group--subtle input,.form .form__group.form__group--subtle select,.form .form__group.form__group--subtle textarea{border-color:#e3e3e3;background-color:transparent}.form .form__group.form__group--subtle input:hover,.form .form__group.form__group--subtle select:hover,.form .form__group.form__group--subtle textarea:hover{border-color:#cdcdcd;background-color:#fff;outline:none}.form .form__group.form__group--subtle input:active,.form .form__group.form__group--subtle input:focus,.form .form__group.form__group--subtle select:active,.form .form__group.form__group--subtle select:focus,.form .form__group.form__group--subtle textarea:active,.form .form__group.form__group--subtle textarea:focus{border-color:#888;background-color:#fff;outline:none}.form .form__fieldset .form__group:last-of-type{margin-bottom:0}.form .form__siblings{float:left;width:100%;margin-bottom:10px}.form .form__siblings:hover .form__group--subtle input,.form .form__siblings:hover .form__group--subtle select,.form .form__siblings:hover .form__group--subtle textarea{border-color:#cdcdcd;background-color:#fff;outline:none}.form .form__siblings.is-active .form__group--subtle input,.form .form__siblings.is-active .form__group--subtle select,.form .form__siblings.is-active .form__group--subtle textarea{border-color:#cdcdcd;background-color:#fff;outline:none}.form.form--stack .form__group{width:100%;float:left}.form.form--stack .form__group [class*='-col']{margin-bottom:0}.form.form--stack .form__group .form__group-label{margin-top:0;margin-bottom:0;line-height:34px;vertical-align:top}.form.form--stack .form__group .form__group-input{min-height:34px}.form.form--stack .form__group .form__group-input .onoffswitch{margin-top:9px}.form.form--inline .form__fieldset{display:inline-block;width:auto;margin:0;vertical-align:middle}.form.form--inline .form__group{display:inline-block;margin:0 20px 0 0;vertical-align:top}.form.form--inline .form__group .form__group-label{margin-bottom:0;margin-right:10px;vertical-align:top;line-height:34px}.form.form--inline .form__group input,.form.form--inline .form__group select,.form.form--inline .form__group .form__group-input{display:inline-block;width:auto;min-height:34px}.form.form--inline .form__group [type='checkbox']+label,.form.form--inline .form__group [type='radio']+label{line-height:34px}.form.form--inline .form__group .onoffswitch{margin:9px 0 0}.form.form--inline .form__group .onoffswitch-external-label{margin:0;vertical-align:middle;line-height:34px}.form .form__help-text{font-size:14px;font-size:0.875rem;color:#888}.form__error{margin:0;color:#df382c}.form__error .form__error-item{margin:6px 0}.code-block{overflow:auto;margin:.5em 0;padding:1em;border:1px solid #cdcdcd;border-radius:.5em}.code-block .code-block__line{float:left;clear:both;margin:0}.code-block.code-block--terminal{color:#fff;border:0;background-color:#333}.code-block.code-block--numbering{counter-reset:line-numbering}.code-block.code-block--numbering .code-block__line::before{width:1.5em;padding-right:1em;content:counter(line-numbering);counter-increment:line-numbering;user-select:none;text-align:right;pointer-events:none;opacity:.5}.flash-messages{width:100%;float:left;margin:0}.flash-messages .flash-messages__item{box-sizing:border-box;border-radius:2px;list-style:none;padding:15px 20px;margin:0 0 20px;background:#fff;background-position:top 50% left 15px;background-repeat:no-repeat;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.flash-messages .flash-messages__item:last-of-type{margin-bottom:0}.flash-messages .flash-messages__item.flash-messages__item--error{background:#fff url("../assets/images/icons/error_colour_white.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.flash-messages .flash-messages__item.flash-messages__item--info{background:#fff url("../assets/images/icons/information_colour_white.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.flash-messages .flash-messages__item.flash-messages__item--warning{background:#fff url("../assets/images/icons/warning_colour_white.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.flash-messages .flash-messages__item.flash-messages__item--success{background:#fff url("../assets/images/icons/success_colour_white.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.icon,.icon--status-failed,.icon--status-in-progress,.icon--status-queued,.icon--status-succeeded,.icon--status-waiting{width:16px;height:16px;padding:0;border-bottom:0 !important;display:inline-block;vertical-align:middle;text-indent:999em}.icon.icon--add,.icon--add.icon--status-failed,.icon--add.icon--status-in-progress,.icon--add.icon--status-queued,.icon--add.icon--status-succeeded,.icon--add.icon--status-waiting{background:url("../assets/images/icons/add.svg") no-repeat;background-size:100% 100%}.icon.icon--account,.icon--account.icon--status-failed,.icon--account.icon--status-in-progress,.icon--account.icon--status-queued,.icon--account.icon--status-succeeded,.icon--account.icon--status-waiting{background:url("../assets/images/icons/account.svg") no-repeat;background-size:100% 100%}.icon.icon--cross,.icon--cross.icon--status-failed,.icon--cross.icon--status-in-progress,.icon--cross.icon--status-queued,.icon--cross.icon--status-succeeded,.icon--cross.icon--status-waiting{background:url("../assets/images/icons/cross.svg") no-repeat;background-size:100% 100%}.icon.icon--cross-orange,.icon--cross-orange.icon--status-failed,.icon--cross-orange.icon--status-in-progress,.icon--cross-orange.icon--status-queued,.icon--cross-orange.icon--status-succeeded,.icon--cross-orange.icon--status-waiting{background:url("../assets/images/icons/cross-orange.svg") no-repeat;background-size:100% 100%}.icon.icon--debug,.icon--debug.icon--status-failed,.icon--debug.icon--status-in-progress,.icon--debug.icon--status-queued,.icon--debug.icon--status-succeeded,.icon--debug.icon--status-waiting{background:url("../assets/images/icons/debug.svg") no-repeat;background-size:100% 100%}.icon.icon--delete,.icon--delete.icon--status-failed,.icon--delete.icon--status-in-progress,.icon--delete.icon--status-queued,.icon--delete.icon--status-succeeded,.icon--delete.icon--status-waiting{background:url("../assets/images/icons/delete.svg") no-repeat;background-size:100% 100%}.icon.icon--edit,.icon--edit.icon--status-failed,.icon--edit.icon--status-in-progress,.icon--edit.icon--status-queued,.icon--edit.icon--status-succeeded,.icon--edit.icon--status-waiting{background:url("../assets/images/icons/edit.svg") no-repeat;background-size:100% 100%}.icon.icon--error,.icon--error.icon--status-failed,.icon--error.icon--status-in-progress,.icon--error.icon--status-queued,.icon--error.icon--status-succeeded,.icon--error.icon--status-waiting{background:url("../assets/images/icons/error.svg") no-repeat;background-size:100% 100%}.icon.icon--error-mono,.icon--error-mono.icon--status-failed,.icon--error-mono.icon--status-in-progress,.icon--error-mono.icon--status-queued,.icon--error-mono.icon--status-succeeded,.icon--error-mono.icon--status-waiting{background:url("../assets/images/icons/error-mono.svg") no-repeat}.icon.icon--help,.icon--help.icon--status-failed,.icon--help.icon--status-in-progress,.icon--help.icon--status-queued,.icon--help.icon--status-succeeded,.icon--help.icon--status-waiting{background:url("../assets/images/icons/help.svg") no-repeat;background-size:100% 100%}.icon.icon--help-mono,.icon--help-mono.icon--status-failed,.icon--help-mono.icon--status-in-progress,.icon--help-mono.icon--status-queued,.icon--help-mono.icon--status-succeeded,.icon--help-mono.icon--status-waiting{background:url("../assets/images/icons/help-mono.svg") no-repeat}.icon.icon--info,.icon--info.icon--status-failed,.icon--info.icon--status-in-progress,.icon--info.icon--status-queued,.icon--info.icon--status-succeeded,.icon--info.icon--status-waiting{background:url("../assets/images/icons/info.svg") no-repeat;background-size:100% 100%}.icon.icon--info-mono,.icon--info-mono.icon--status-failed,.icon--info-mono.icon--status-in-progress,.icon--info-mono.icon--status-queued,.icon--info-mono.icon--status-succeeded,.icon--info-mono.icon--status-waiting{background:url("../assets/images/icons/info-mono.svg") no-repeat}.icon.icon--loading,.icon--loading.icon--status-failed,.icon--loading.icon--status-in-progress,.icon--loading.icon--status-queued,.icon--loading.icon--status-succeeded,.icon--loading.icon--status-waiting{background:url("../assets/images/icons/loading.png") no-repeat;background-size:100% 100%}.icon.icon--mount,.icon--mount.icon--status-failed,.icon--mount.icon--status-in-progress,.icon--mount.icon--status-queued,.icon--mount.icon--status-succeeded,.icon--mount.icon--status-waiting{background:url("../assets/images/icons/mount.svg") no-repeat;background-size:100% 100%}.icon.icon--unmount,.icon--unmount.icon--status-failed,.icon--unmount.icon--status-in-progress,.icon--unmount.icon--status-queued,.icon--unmount.icon--status-succeeded,.icon--unmount.icon--status-waiting{background:url("../assets/images/icons/unmount.svg") no-repeat;background-size:100% 100%}.icon.icon--partition,.icon--partition.icon--status-failed,.icon--partition.icon--status-in-progress,.icon--partition.icon--status-queued,.icon--partition.icon--status-succeeded,.icon--partition.icon--status-waiting{background:url("../assets/images/icons/partition.svg") no-repeat;background-size:100% 100%}.icon.icon--power-error,.icon--power-error.icon--status-failed,.icon--power-error.icon--status-in-progress,.icon--power-error.icon--status-queued,.icon--power-error.icon--status-succeeded,.icon--power-error.icon--status-waiting{background:url("../assets/images/icons/power-error.svg") no-repeat;background-size:100% 100%}.icon.icon--power-off,.icon--power-off.icon--status-failed,.icon--power-off.icon--status-in-progress,.icon--power-off.icon--status-queued,.icon--power-off.icon--status-succeeded,.icon--power-off.icon--status-waiting{background:url("../assets/images/icons/power-off.svg") no-repeat;background-size:100% 100%}.icon.icon--power-on,.icon--power-on.icon--status-failed,.icon--power-on.icon--status-in-progress,.icon--power-on.icon--status-queued,.icon--power-on.icon--status-succeeded,.icon--power-on.icon--status-waiting{background:url("../assets/images/icons/power-on.svg") no-repeat;background-size:100% 100%}.icon.icon--remove,.icon--remove.icon--status-failed,.icon--remove.icon--status-in-progress,.icon--remove.icon--status-queued,.icon--remove.icon--status-succeeded,.icon--remove.icon--status-waiting{background:url("../assets/images/icons/remove.svg") no-repeat;background-size:100% 100%}.icon.icon--success,.icon--success.icon--status-failed,.icon--success.icon--status-in-progress,.icon--success.icon--status-queued,.icon--success.icon--status-succeeded,.icon--success.icon--status-waiting{background:url("../assets/images/icons/success.svg") no-repeat;background-size:100% 100%}.icon.icon--success-mono,.icon--success-mono.icon--status-failed,.icon--success-mono.icon--status-in-progress,.icon--success-mono.icon--status-queued,.icon--success-mono.icon--status-succeeded,.icon--success-mono.icon--status-waiting{background:url("../assets/images/icons/success-mono.svg") no-repeat;background-size:100% 100%}.icon.icon--success-grey,.icon--success-grey.icon--status-failed,.icon--success-grey.icon--status-in-progress,.icon--success-grey.icon--status-queued,.icon--success-grey.icon--status-succeeded,.icon--success-grey.icon--status-waiting{background:url('data:image/svg+xml;utf8, ');background-size:100% 100%}.icon.icon--settings,.icon--settings.icon--status-failed,.icon--settings.icon--status-in-progress,.icon--settings.icon--status-queued,.icon--settings.icon--status-succeeded,.icon--settings.icon--status-waiting{background:url("../assets/images/icons/settings.svg") no-repeat;background-size:100% 100%}.icon.icon--sync,.icon--sync.icon--status-failed,.icon--sync.icon--status-in-progress,.icon--sync.icon--status-queued,.icon--sync.icon--status-succeeded,.icon--sync.icon--status-waiting{background:url("../assets/images/icons/sync.svg") no-repeat;background-size:100% 100%}.icon.icon--search,.icon--search.icon--status-failed,.icon--search.icon--status-in-progress,.icon--search.icon--status-queued,.icon--search.icon--status-succeeded,.icon--search.icon--status-waiting{background:url("../assets/images/icons/magnifying_glass.svg") no-repeat;background-size:100% 100%}.icon.icon--system-shutdown,.icon--system-shutdown.icon--status-failed,.icon--system-shutdown.icon--status-in-progress,.icon--system-shutdown.icon--status-queued,.icon--system-shutdown.icon--status-succeeded,.icon--system-shutdown.icon--status-waiting{background:url("../assets/images/icons/system-shutdown.svg") no-repeat;background-size:100% 100%}.icon.icon--tooltip,.icon--tooltip.icon--status-failed,.icon--tooltip.icon--status-in-progress,.icon--tooltip.icon--status-queued,.icon--tooltip.icon--status-succeeded,.icon--tooltip.icon--status-waiting{background:url("../assets/images/icons/tooltip.svg") no-repeat;background-size:100% 100%}.icon.icon--tags,.icon--tags.icon--status-failed,.icon--tags.icon--status-in-progress,.icon--tags.icon--status-queued,.icon--tags.icon--status-succeeded,.icon--tags.icon--status-waiting{background:url("../assets/images/icons/tags.svg") no-repeat;background-size:100% 100%}.icon.icon--tick,.icon--tick.icon--status-failed,.icon--tick.icon--status-in-progress,.icon--tick.icon--status-queued,.icon--tick.icon--status-succeeded,.icon--tick.icon--status-waiting{background:url("../assets/images/icons/tick.svg") no-repeat;background-size:100% 100%}.icon.icon--logical-volume,.icon--logical-volume.icon--status-failed,.icon--logical-volume.icon--status-in-progress,.icon--logical-volume.icon--status-queued,.icon--logical-volume.icon--status-succeeded,.icon--logical-volume.icon--status-waiting{background:url("../assets/images/icons/logical-volume.svg") no-repeat;background-size:100% 100%}.icon.icon--warning,.icon--warning.icon--status-failed,.icon--warning.icon--status-in-progress,.icon--warning.icon--status-queued,.icon--warning.icon--status-succeeded,.icon--warning.icon--status-waiting{background:url("../assets/images/icons/warning.svg") no-repeat;background-size:100% 100%}.icon.icon--warning-mono,.icon--warning-mono.icon--status-failed,.icon--warning-mono.icon--status-in-progress,.icon--warning-mono.icon--status-queued,.icon--warning-mono.icon--status-succeeded,.icon--warning-mono.icon--status-waiting{background:url("../assets/images/icons/warning-mono.svg") no-repeat}.icon.icon--open,.icon--open.icon--status-failed,.icon--open.icon--status-in-progress,.icon--open.icon--status-queued,.icon--open.icon--status-succeeded,.icon--open.icon--status-waiting{background:url("../assets/images/forms/chevron-down.svg") no-repeat;background-size:10px 4px;background-position:center 4px}.icon.icon--close,.icon--close.icon--status-failed,.icon--close.icon--status-in-progress,.icon--close.icon--status-queued,.icon--close.icon--status-succeeded,.icon--close.icon--status-waiting{background:url("../assets/images/forms/chevron-up.svg") no-repeat;background-size:10px 4px;background-position:center 4px}.icon--status-failed{content:url('data:image/svg+xml;utf8, ')}.icon--status-in-progress{content:url('data:image/svg+xml;utf8, ')}.icon--status-queued{content:url('data:image/svg+xml;utf8, ')}.icon--status-succeeded{content:url('data:image/svg+xml;utf8, ')}.icon--status-waiting{content:url('data:image/svg+xml;utf8, ')}.icon--small{width:14px;height:14px}.icon--large{width:20px;height:20px}.icon--mega{width:23px;height:23px}.icon--gigantic{width:32px;height:32px}h1 .icon,h1 .icon--status-failed,h1 .icon--status-in-progress,h1 .icon--status-queued,h1 .icon--status-succeeded,h1 .icon--status-waiting{margin-top:-6px}h2 .icon,h2 .icon--status-failed,h2 .icon--status-in-progress,h2 .icon--status-queued,h2 .icon--status-succeeded,h2 .icon--status-waiting,h3 .icon,h3 .icon--status-failed,h3 .icon--status-in-progress,h3 .icon--status-queued,h3 .icon--status-succeeded,h3 .icon--status-waiting{margin-top:-5px}h4 .icon,h4 .icon--status-failed,h4 .icon--status-in-progress,h4 .icon--status-queued,h4 .icon--status-succeeded,h4 .icon--status-waiting,h5 .icon,h5 .icon--status-failed,h5 .icon--status-in-progress,h5 .icon--status-queued,h5 .icon--status-succeeded,h5 .icon--status-waiting,p .icon,p .icon--status-failed,p .icon--status-in-progress,p .icon--status-queued,p .icon--status-succeeded,p .icon--status-waiting,label .icon,label .icon--status-failed,label .icon--status-in-progress,label .icon--status-queued,label .icon--status-succeeded,label .icon--status-waiting,button .icon,button .icon--status-failed,button .icon--status-in-progress,button .icon--status-queued,button .icon--status-succeeded,button .icon--status-waiting,a .icon,a .icon--status-failed,a .icon--status-in-progress,a .icon--status-queued,a .icon--status-succeeded,a .icon--status-waiting,th .icon,th .icon--status-failed,th .icon--status-in-progress,th .icon--status-queued,th .icon--status-succeeded,th .icon--status-waiting,td .icon,td .icon--status-failed,td .icon--status-in-progress,td .icon--status-queued,td .icon--status-succeeded,td .icon--status-waiting,.table__header .icon,.table__header .icon--status-failed,.table__header .icon--status-in-progress,.table__header .icon--status-queued,.table__header .icon--status-succeeded,.table__header .icon--status-waiting,.table__data .icon,.table__data .icon--status-failed,.table__data .icon--status-in-progress,.table__data .icon--status-queued,.table__data .icon--status-succeeded,.table__data .icon--status-waiting{margin-top:-3px}.list__tree{list-style:none;border-left:1px solid #d2d2d2;position:relative}.list__tree.list__tree--sub-level{margin-top:10px;margin-left:20px;clear:both}.list__tree.list__tree--sub-level .list__item .list__item-feedback{left:180px}.list__tree .list__item{list-style:none}.list__tree .list__item:before{content:'';width:12px;height:1px;background:#d2d2d2;display:inline-block;position:relative;top:-4px;margin-right:5px}.list__tree .list__item:last-child::after{content:'';width:4px;height:1em;position:absolute;display:block;left:-2px;bottom:-6px;background:#fafafa}.list__tree .list__item .list__item-feedback{position:relative;left:200px;margin-top:-24px}table,.table{border-color:#d2d2d2;border-spacing:0;overflow-x:scroll;margin:0 0 20px;width:100%;text-align:left;border-collapse:separate}table tr,.table table tr,table .table tr,.table .table__row{width:100%;border-color:#b2b2b2;border-bottom-style:dotted;border-bottom-width:1px}table th,.table table th,table .table th,table td,.table table td,table .table td,.table .table__header,.table .table__data{font-size:16px;font-size:1rem;padding:10px;box-sizing:border-box;min-height:21px;background:none;border:0;text-align:left;border-collapse:separate;vertical-align:top;backface-visibility:hidden;position:relative}table thead tr,.table table thead tr,table thead .table tr,.table .table__head .table__row{color:#888;border-bottom:1px solid}table thead tr:hover,.table .table__head .table__row:hover{background-color:transparent}table thead th,.table table thead th,table thead .table th,.table .table__head .table__header{font-size:13px;font-size:0.8125rem;background:none;color:#888;font-size:13px}table thead th input[type="radio"]+label,.table .table__head .table__header input[type="radio"]+label,table thead th input[type="checkbox"]+label,.table .table__head .table__header input[type="checkbox"]+label{margin:0;top:-3px}table thead th a:link,table thead th a:visited,.table .table__head .table__header .table__header-link{color:#888;border-bottom:0}table thead th a:hover:link,table thead th a:hover:visited,.table .table__head .table__header .table__header-link:hover{color:#333;text-decoration:none}table thead th a.is-active:link,table thead th a.is-active:visited,.table .table__head .table__header .is-active.table__header-link{color:#333;text-decoration:none}table thead th a.is-sorted:link,table thead th a.is-sorted:visited,.table .table__head .table__header .is-sorted.table__header-link{border-bottom:1px solid #333}table thead .divide,.table .table__head .table__header-divide,.table .table__head .divide{width:1px;display:inline-block;background:#888;height:10px;margin:0 5px}table .numerical,.table .numerical{text-align:right}table input[type="radio"]+label,.table input[type="radio"]+label,table input[type="checkbox"]+label,.table input[type="checkbox"]+label{margin:0;top:-1px}table th{color:#888;border-bottom:1px solid}table td{border-color:#b2b2b2;border-bottom-style:dotted;border-bottom-width:1px}table td a:link,table td a:visited{color:#333;border-bottom:1px solid #c2c2c2}table td a:link:hover,table td a:visited:hover{text-decoration:none;color:#dd4814}.table{display:table}.table .table__row{float:left;display:table-row}.table .table__row:hover{background-color:#fff}.table .table__row:hover .table__controls{z-index:1;opacity:1}.table .table__row:hover .table__controls--secondary{z-index:1;opacity:1}.table .table__row.is-active{background-color:#fff}.table .table__row.is-active .table__controls{z-index:1;opacity:1}.table .table__row.is-active .table__controls--secondary{z-index:1;opacity:1}.table .table__row.is-active .table__dropdown .table__row{display:none}.table .table__row.is-active .table__dropdown .table__row.is-active{display:block}.table .table__header,.table .table__data{display:table-cell;float:left}.table .table__data a:link,.table .table__data a:visited{color:#333;border-bottom:1px solid #c2c2c2}.table .table__data a:link:hover,.table .table__data a:visited:hover{text-decoration:none;color:#dd4814}.table .table__head{display:table-head;width:100%;box-sizing:border-box}.table .table__body{display:table-row-group}.table .table__footer{display:table-footer-group}.table .table__label{clear:both;display:block;margin-top:5px;line-height:16px;color:#bcbcbc}.table .table__label a{color:#bcbcbc}.table .table__label a:hover{color:#f7f7f7}.table .table__label.is-active a{color:#f7f7f7}.table .table__controls{width:100%;text-align:right;opacity:0;z-index:-1000}.table .table__controls--secondary{opacity:0;z-index:-1000;width:auto;text-align:left}.table .table__controls a,.table .table__controls a:link,.table .table__controls a:visited{color:#333;border-bottom:1px solid #c2c2c2}.table .table__controls a:hover,.table .table__controls a:link:hover,.table .table__controls a:visited:hover{text-decoration:none;color:#dd4814}.table .table__input{display:inline-block;background-color:#fff;border-color:#d2d2d2;background-position:right 10px top 16px;margin:-7px 0}.table .table__input[disabled]{background-color:transparent;border-color:transparent;pointer-events:none;background-position:-9999px -9999px;color:#333}.table .table__dropdown{width:100%}.table .table__dropdown .table__row{border-bottom:0;display:none;position:relative}.table .table__dropdown .table__row--indent{padding-left:41px !important;float:left;width:100%;box-sizing:border-box}.table .table__dropdown .table__row:before{display:block;margin:0 auto;width:calc(100% - 20px);border-top:1px dotted #d2d2d2;position:absolute;height:1px;content:'';top:0;left:10px}.table .table__dropdown .table__row.table__row--head{border-bottom:0}.table .table__dropdown .table__row.table__row--head .table__header{color:#bcbcbc;font-size:13px}.table .table__dropdown .table__row.u-border--none:before{display:none}.table .table__dropdown .table__row.u-border:before{display:block;margin:0 auto;width:calc(100% - 20px);border-top:1px dotted #d2d2d2;position:absolute;height:1px;content:'';top:0;left:10px}.table .table__dropdown .table__row.is-active .table__input{background-color:#fff;border-color:#d2d2d2;background-position:right 10px top 16px;pointer-events:all}.table .table__dropdown .table__row.is-active .table__input[disabled]{border-color:transparent;cursor:pointer}.table .table__dropdown--info .table__row{border-bottom:0}.table .table__dropdown--info .table__data{color:#bcbcbc}.form .form__group input,.form .form__group select{margin:0}.table--error{border-color:#df382c}.table--error .table__header,.table--error .table__data,.table--error th,.table--error td{border-color:#df382c;background-color:#fadfdd}.table--warning{border-color:#eca918}.table--warning .table__header,.table--warning .table__data,.table--warning th,.table--warning td{border-color:#eca918;background-color:#fcefd4}.table--success{border-color:#38b44a}.table--success .table__header,.table--success .table__data,.table--success th,.table--success td{border-color:#38b44a;background-color:#caeecf}.table--information{border-color:#19b6ee}.table--information .table__header,.table--information .table__data,.table--information th,.table--information td{border-color:#19b6ee;background-color:#d7f2fc}.table-col--1{width:1%}.table-col--2{width:2%}.table-col--3{width:3%}.table-col--4{width:4%}.table-col--5{width:5%}.table-col--6{width:6%}.table-col--7{width:7%}.table-col--8{width:8%}.table-col--9{width:9%}.table-col--10{width:10%}.table-col--11{width:11%}.table-col--12{width:12%}.table-col--13{width:13%}.table-col--14{width:14%}.table-col--15{width:15%}.table-col--16{width:16%}.table-col--17{width:17%}.table-col--18{width:18%}.table-col--19{width:19%}.table-col--20{width:20%}.table-col--21{width:21%}.table-col--22{width:22%}.table-col--23{width:23%}.table-col--24{width:24%}.table-col--25{width:25%}.table-col--26{width:26%}.table-col--27{width:27%}.table-col--28{width:28%}.table-col--29{width:29%}.table-col--30{width:30%}.table-col--31{width:31%}.table-col--32{width:32%}.table-col--33{width:33%}.table-col--34{width:34%}.table-col--35{width:35%}.table-col--36{width:36%}.table-col--37{width:37%}.table-col--38{width:38%}.table-col--39{width:39%}.table-col--40{width:40%}.table-col--41{width:41%}.table-col--42{width:42%}.table-col--43{width:43%}.table-col--44{width:44%}.table-col--45{width:45%}.table-col--46{width:46%}.table-col--47{width:47%}.table-col--48{width:48%}.table-col--49{width:49%}.table-col--50{width:50%}.table-col--51{width:51%}.table-col--52{width:52%}.table-col--53{width:53%}.table-col--54{width:54%}.table-col--55{width:55%}.table-col--56{width:56%}.table-col--57{width:57%}.table-col--58{width:58%}.table-col--59{width:59%}.table-col--60{width:60%}.table-col--61{width:61%}.table-col--62{width:62%}.table-col--63{width:63%}.table-col--64{width:64%}.table-col--65{width:65%}.table-col--66{width:66%}.table-col--67{width:67%}.table-col--68{width:68%}.table-col--69{width:69%}.table-col--70{width:70%}.table-col--71{width:71%}.table-col--72{width:72%}.table-col--73{width:73%}.table-col--74{width:74%}.table-col--75{width:75%}.table-col--76{width:76%}.table-col--77{width:77%}.table-col--78{width:78%}.table-col--79{width:79%}.table-col--80{width:80%}.table-col--81{width:81%}.table-col--82{width:82%}.table-col--83{width:83%}.table-col--84{width:84%}.table-col--85{width:85%}.table-col--86{width:86%}.table-col--87{width:87%}.table-col--88{width:88%}.table-col--89{width:89%}.table-col--90{width:90%}.table-col--91{width:91%}.table-col--92{width:92%}.table-col--93{width:93%}.table-col--94{width:94%}.table-col--95{width:95%}.table-col--96{width:96%}.table-col--97{width:97%}.table-col--98{width:98%}.table-col--99{width:99%}.table-col--100{width:100%}.tabs{display:inline-block}.tabs .tabs__tab{font-size:18px;font-size:1.125rem;display:inline-block;list-style-type:none}.tabs .tabs__tab .tabs__tab-link{color:#888;text-decoration:none;border-bottom:0}.tabs .tabs__tab:hover .tabs__tab-link{border-bottom:3px solid #888}.tabs .tabs__tab:after{content:'';width:1px;display:inline-block;background:#d2d2d2;height:11px;padding:0;margin:0 5px}.tabs .tabs__tab:last-child::after{display:none}.tabs .is-active .tabs__tab-link,.tabs .is-active:hover .tabs__tab-link{border-bottom:3px solid #dd4814}.tooltip{position:relative}.tooltip::before{position:absolute;left:50%;transform:translateX(-50%) translateY(-14px);content:'';width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;z-index:-1;border-top:5px solid #333;opacity:0;visibility:hidden;bottom:100%;margin-bottom:-11px}.tooltip::after{content:attr(aria-label);font-size:13px;font-weight:400;line-height:16px;position:absolute;z-index:-1;left:50%;bottom:100%;transform:translateX(-50%) translateY(-8px);background:#333;color:#fff;padding:10px;height:auto;text-indent:0;opacity:0;visibility:hidden;border-radius:5px;box-shadow:0 1px 3px 0 rgba(51,51,51,0.2);white-space:pre;box-sizing:border-box;font-style:normal}.tooltip:hover::after,.tooltip:hover::before{opacity:1 !important;z-index:1000;visibility:visible}.tooltip.tooltip--right::before{border-top:5px solid transparent;border-right:5px solid #333;border-bottom:5px solid transparent;left:100%;bottom:inherit;top:50%;transform:translateX(4px) translateY(-50%)}.tooltip.tooltip--right::after{left:100%;bottom:inherit;top:50%;margin-bottom:-14px;transform:translateX(14px) translateY(-50%)}.tooltip.tooltip--bottom::before{border-top:0;border-right:5px solid transparent;border-bottom:5px solid #333;border-left:5px solid transparent;left:50%;bottom:inherit;top:100%;transform:translateX(-50%) translateY(8px)}.tooltip.tooltip--bottom::after{left:50%;bottom:inherit;top:100%;margin-bottom:0;transform:translateX(-50%) translateY(13px)}.tooltip.tooltip--left::before{border-top:5px solid transparent;border-left:5px solid #333;border-bottom:5px solid transparent;left:-4px;bottom:inherit;top:50%;transform:translateX(-100%) translateY(-50%)}.tooltip.tooltip--left::after{left:-14px;bottom:inherit;top:50%;margin-bottom:-14px;transform:translateX(-100%) translateY(-50%)}.accordion{box-sizing:border-box;border-radius:2px;list-style:none;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);margin-bottom:40px}.accordion.is-disabled{opacity:.5;pointer-events:none}.accordion .accordion__title{font-size:20px;font-size:1.25rem;border-bottom:1px dotted #b2b2b2;padding:8px 20px 9px;margin:0}.accordion .accordion__tab{border-bottom:1px dotted #b2b2b2}.accordion .accordion__tab:last-of-type{border:0}.accordion .accordion__tab .accordion__tab-title.is-active{background-image:url("../assets/images/forms/chevron_up.svg")}.accordion .accordion__tab .accordion__tab-title.is-active+.accordion__tab-content{max-height:400px;transition:max-height .5s ease-in}.accordion .accordion__tab .accordion__tab-content{max-height:0;transition:max-height .5s ease-out;overflow:hidden;overflow:auto}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list{list-style-type:none;padding:0 20px 14px;margin:0}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item{margin-bottom:.15em}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item .accordion__tab-link{box-sizing:border-box;color:#333;width:100%;display:inline-block;padding-right:20px;border-bottom:0;outline:0}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item .accordion__tab-link:hover{color:#dd4814;text-decoration:none}.disabled .accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item .accordion__tab-link{color:#333}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active{margin-bottom:.15em;font-weight:400}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active .accordion__tab-link{background:transparent url("../assets/images/icons/cross.svg") top 7px right 0 no-repeat;text-decoration:none}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active:hover{color:#f7f7f7}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active:hover .accordion__tab-link{color:#dd4814;background-image:url("../assets/images/icons/cross_orange.svg")}.accordion .accordion__tab .accordion__tab-title,.accordion .accordion__tab .accordion__tab-title.is-active{padding:12px 20px;margin:0;color:#888;cursor:pointer;background:transparent url("../assets/images/forms/chevron_down.svg") top 20px right 20px no-repeat}.action-card{background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);margin-bottom:40px;padding:20px 20px 10px;box-sizing:border-box;width:100%;display:inline-block;clear:both}.action-card .action-card__title{font-size:23px;margin-bottom:20px}.action-card .action-card__controls{border-top:1px dotted #888;width:100%;display:inline-block;margin:10px 0 0;padding:10px 0 0;box-sizing:border-box}.banner{background-color:#000}@media only screen and (min-width: 620px){.banner{box-shadow:none}.banner .nav-primary{max-width:none}.banner .nav-primary .logo{height:auto;margin-top:12px}.banner .nav-primary .logo a{border-bottom:0}.banner .nav-primary ul li{border-left:1px solid #333}.banner .nav-primary ul li:last-child{border-right:1px solid #333;border-left:1px solid #333}.banner .nav-primary ul li a,.banner .nav-primary ul li a:link,.banner .nav-primary ul li a:visited{box-sizing:border-box;border-bottom:0;border-left:0}.banner .nav-primary ul li a:hover,.banner .nav-primary ul li a:link:hover,.banner .nav-primary ul li a:visited:hover{background-color:rgba(51,51,51,0.9)}.banner .nav-primary ul li a.active,.banner .nav-primary ul li a:link.active,.banner .nav-primary ul li a:visited.active{background-color:transparent;box-shadow:inset 0px -3px #dd4814}.banner .nav-primary ul li a.active:hover,.banner .nav-primary ul li a:link.active:hover,.banner .nav-primary ul li a:visited.active:hover{background-color:rgba(51,51,51,0.9)}}.page-header{box-sizing:border-box;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);padding:0 20px;width:100%;float:left;margin-bottom:20px}.page-header .page-header__title{font-size:32px;font-size:2rem;margin:0;padding:34px 0;display:inline-block}.page-header .page-header__title [contenteditable="true"]{font-size:32px;font-size:2rem;padding:8px 10px;width:auto;box-sizing:border-box;border:1px solid transparent;margin:-20px 0 -20px -10px;border-radius:2px;color:#333;cursor:default;display:inline-block}.page-header .page-header__title [contenteditable="true"].editmode,.page-header .page-header__title [contenteditable="true"].editable:hover{border:1px solid #D2D2D2;cursor:text}.page-header .page-header__title [contenteditable="true"].editmode:hover,.page-header .page-header__title [contenteditable="true"]:active,.page-header .page-header__title [contenteditable="true"]:focus{outline:none;background-color:#fff;border:1px solid #B2B2B2}.page-header .page-header__title [contenteditable="true"].invalid,.page-header .page-header__title [contenteditable="true"].invalid:hover,.page-header .page-header__title [contenteditable="true"].invalid:active,.page-header .page-header__title [contenteditable="true"].invalid:focus{border-color:#df382c}.page-header .page-header__title .page-header__title-dot{display:inline-block;width:auto;padding:0}.page-header .page-header__title .page-header__title-domain{font-size:32px;font-size:2rem;display:inline-block;width:auto;min-height:66px;max-height:66px;line-height:25px;margin:-20px 0;background-position:top 32px right 10px;padding:13px 10px !important}.page-header .page-header__status{font-size:19px;font-size:1.1875rem;width:auto;display:inline-block;position:relative;margin-left:20px;color:#888}.page-header .page-header__status .page-header__status-check{font-size:16px;font-size:1rem;margin-left:10px;color:inherit;border:0}.page-header .page-header__status .page-header__status-check:hover{text-decoration:underline}.page-header .page-header__controls{padding:40px 0;display:inline-block;float:right}.page-header .page-header__controls .page-header__controls-feedback{color:#333;text-decoration:none;border-bottom:1px solid #c2c2c2}.page-header .page-header__controls .page-header__controls-feedback:hover{text-decoration:none;color:#dd4814;cursor:pointer}.page-header .page-header__dropdown{max-height:0;overflow:hidden;-webkit-transition:max-height 0.3s ease;-moz-transition:max-height 0.3s ease;transition:max-height 0.3s ease}.page-header .page-header__dropdown.is-open{max-height:1000px}.page-header .page-header__dropdown .page-header__section{border-top:1px dotted #888;padding-top:20px;padding-bottom:20px}.page-header .page-header__dropdown .page-header__message{width:auto;display:inline-block;float:none;margin:0 20px 0 0;line-height:36px}.page-header .page-header__dropdown .page-header__message--error{background:url("../assets/images/icons/error.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__message--info{background:url("../assets/images/icons/info.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__message--warning{background:url("../assets/images/icons/warning.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__message--success{background:url("../assets/images/icons/success.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__controls{padding:0;float:right}.search{position:relative}.search .search__input{font-size:16px;font-size:1rem;box-sizing:border-box;border-radius:4px;list-style:none;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);width:100%;border:0;padding:15px;appearance:textfield}.search .search__input::placeholder{color:#888}.search .search__input::-ms-clear{display:none}.search .search__input::-webkit-search-decoration,.search .search__input::-webkit-search-cancel-button,.search .search__input::-webkit-search-results-button,.search .search__input::-webkit-search-results-decoration{display:none}.search .search__input:disabled,.search .search__input.search--disabled{background-color:#fff;opacity:.5;pointer-events:none}.search .search__input:disabled+.search__submit,.search .search__input.search--disabled+.search__submit{pointer-events:none;opacity:.5}.search .search__submit{position:absolute;top:15px;right:15px;background-color:transparent;background-image:url("../assets/images/icons/magnifying_glass.svg");background-position:center;background-repeat:no-repeat;background-size:20px;text-indent:-999em;display:block;width:20px;height:20px;overflow:hidden;outline:none;padding:0;border:0}.search .search__submit:hover{background-color:transparent;background-image:url("../assets/images/icons/magnifying_glass.svg")}.search .search__submit.search__submit--close{background-image:url("../assets/images/icons/remove.svg");background-size:16px}.u-animation--spin{animation:spin 1s infinite linear !important}.u-animation--pulse{animation:pulse 1s ease-out !important;animation-iteration-count:infinite !important;opacity:0}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@keyframes pulse{0%{transform:scale(0.1, 0.1);opacity:0}50%{opacity:1}100%{transform:scale(1.2, 1.2);opacity:0}}.u-border{border:1px dotted #cdcdcd !important}.u-border--top{border-top:1px dotted #cdcdcd !important}.u-border--right{border-right:1px dotted #cdcdcd !important}.u-border--bottom{border-bottom:1px dotted #cdcdcd !important}.u-border--left{border-left:1px dotted #cdcdcd !important}.u-border--dotted{border-style:dotted !important}.u-border--solid{border-style:solid !important}.u-border--dashed{border-style:dashed !important}.u-border--none{border:0 !important}.u-align--right{text-align:right !important}.u-align--left{text-align:left !important}.u-align--center{text-align:center !important}.u-float--right{float:right !important}.u-float--left{float:left !important}.u-float--none{float:none !important}.u-muted{opacity:.5 !important;filter:alpha(opacity=50) !important}.u-clear{clear:both !important}.u-display--block{display:block !important}.u-display--inline{display:inline !important}.u-display--inline-block{display:inline-block !important}.u-display--hidden{display:none !important}.u-width--full{width:100% !important}.u-width--half{width:50% !important}.u-width--third{width:calc(100%/3) !important}.u-width--quater{width:25% !important}.u-margin{margin:20px}.u-margin--none{margin:0 !important}.u-margin--tiny{margin:5px !important}.u-margin--small{margin:10px !important}.u-margin--large{margin:40px !important}.u-margin--huge{margin:80px !important}.u-margin--top{margin-top:20px !important}.u-margin--top-none{margin-top:0 !important}.u-margin--top-tiny{margin-top:5px !important}.u-margin--top-small{margin-top:10px !important}.u-margin--top-large{margin-top:40px !important}.u-margin--top-huge{margin-top:80px !important}.u-margin--right{margin-right:20px !important}.u-margin--right-none{margin-right:0 !important}.u-margin--right-tiny{margin-right:5px !important}.u-margin--right-small{margin-right:10px !important}.u-margin--right-large{margin-right:40px !important}.u-margin--right-huge{margin-right:80px !important}.u-margin--bottom{margin-bottom:20px !important}.u-margin--bottom-none{margin-bottom:0 !important}.u-margin--bottom-tiny{margin-bottom:5px !important}.u-margin--bottom-small{margin-bottom:10px !important}.u-margin--bottom-large{margin-bottom:40px !important}.u-margin--bottom-huge{margin-bottom:80px !important}.u-margin--left{margin-left:20px !important}.u-margin--left-none{margin-left:0 !important}.u-margin--left-tiny{margin-left:5px !important}.u-margin--left-small{margin-left:10px !important}.u-margin--left-large{margin-left:40px !important}.u-margin--left-huge{margin-left:80px !important}.u-padding{padding:20px}.u-padding--none{padding:0 !important}.u-padding--tiny{padding:5px !important}.u-padding--small{padding:10px !important}.u-padding--large{padding:40px !important}.u-padding--huge{padding:80px !important}.u-padding--top{padding-top:20px !important}.u-padding--top-none{padding-top:0 !important}.u-padding--top-tiny{padding-top:5px !important}.u-padding--top-small{padding-top:10px !important}.u-padding--top-large{padding-top:40px !important}.u-padding--top-huge{padding-top:80px !important}.u-padding--right{padding-right:20px !important}.u-padding--right-none{padding-right:0 !important}.u-padding--right-tiny{padding-right:5px !important}.u-padding--right-small{padding-right:10px !important}.u-padding--right-large{padding-right:40px !important}.u-padding--right-huge{padding-right:80px !important}.u-padding--bottom{padding-bottom:20px !important}.u-padding--bottom-none{padding-bottom:0 !important}.u-padding--bottom-tiny{padding-bottom:5px !important}.u-padding--bottom-small{padding-bottom:10px !important}.u-padding--bottom-large{padding-bottom:40px !important}.u-padding--bottom-huge{padding-bottom:80px !important}.u-padding--left{padding-left:20px !important}.u-padding--left-none{padding-left:0 !important}.u-padding--left-tiny{padding-left:5px !important}.u-padding--left-small{padding-left:10px !important}.u-padding--left-large{padding-left:40px !important}.u-padding--left-huge{padding-left:80px !important}.u-text--subtle{color:#888;font-size:.9375rem}.u-text--error{color:#df382c !important}.u-border--error{border-color:#df382c !important}.u-background--error{background-color:#df382c !important}.has-error{border:1px solid #df382c !important}.has-error:focus{border:1px solid #df382c !important}.u-text--off{color:#d2d2d2 !important}.u-border--off{border-color:#d2d2d2 !important}.u-background--off{background-color:#d2d2d2 !important}.has-off{border:1px solid #d2d2d2 !important}.has-off:focus{border:1px solid #d2d2d2 !important}.u-text--warning{color:#eca918 !important}.u-border--warning{border-color:#eca918 !important}.u-background--warning{background-color:#eca918 !important}.has-warning{border:1px solid #eca918 !important}.has-warning:focus{border:1px solid #eca918 !important}.u-text--success{color:#38b44a !important}.u-border--success{border-color:#38b44a !important}.u-background--success{background-color:#38b44a !important}.has-success{border:1px solid #38b44a !important}.has-success:focus{border:1px solid #38b44a !important}.u-text--on{color:#38b44a !important}.u-border--on{border-color:#38b44a !important}.u-background--on{background-color:#38b44a !important}.has-on{border:1px solid #38b44a !important}.has-on:focus{border:1px solid #38b44a !important}.u-text--information{color:#19b6ee !important}.u-border--information{border-color:#19b6ee !important}.u-background--information{background-color:#19b6ee !important}.has-information{border:1px solid #19b6ee !important}.has-information:focus{border:1px solid #19b6ee !important}.u-text--loading{color:#2ab7ec !important}.u-border--loading{border-color:#2ab7ec !important}.u-background--loading{background-color:#2ab7ec !important}.has-loading{border:1px solid #2ab7ec !important}.has-loading:focus{border:1px solid #2ab7ec !important}.u-vertical-align{position:relative;top:50%;transform:translateY(-50%)}.u-text--truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.hidden{display:none}.small-icon{width:12px}.images-info{text-align:center;padding:10px}.images-warning{box-sizing:border-box;border-radius:2px;list-style:none;padding:15px 20px 15px 45px;background-repeat:no-repeat;box-shadow:0 1px 1px rgba(0,0,0,0.1);background:#fff url("../assets/images/icons/error.svg") no-repeat;background-size:16px 16px;background-position:15px center;margin-bottom:20px}#loader{width:10px;margin:16px auto 0 auto}#importing{box-sizing:border-box;border-radius:2px;list-style:none;padding:15px;font-size:16px;margin:0 0 20px;background:#fff;background-position:top 50% left 15px;background-repeat:no-repeat;box-shadow:0 1px 1px rgba(0,0,0,0.1)}#importing .spinner{margin-top:-3px}.spinner{width:16px;height:16px;padding:0;border-bottom:0 !important;display:inline-block;vertical-align:middle;text-indent:999em;background:url("../assets/images/icons/loading.png") no-repeat;background-size:100% 100%}.spin{animation:spin 1s infinite linear !important}.importing-dot{opacity:0;-webkit-animation:dot 1.3s infinite;animation:dot 1.3s infinite}.accounts .logout .divide{padding:0 20px 0 30px;display:inline-block}.accounts .api li{position:relative}.accounts .api li input[type='text']{line-height:30px;padding-right:30px;width:100%}.accounts .api li input[type='text']::-webkit-input-placeholder{color:#333}.accounts .api li input[type='text']:-moz-placeholder{color:#333}.accounts .api li input[type='text']::-moz-placeholder{color:#333}.accounts .api li input[type='text']:-ms-input-placeholder{color:#333}.accounts .api li .delete-link{position:absolute;top:13px;right:15px}.script-content{width:100%;overflow:hidden}.script-content .content{margin:20px 0 0} +*{font-smoothing:subpixel-antialiased;box-sizing:border-box}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{font-smoothing:subpixel-antialiased;font-size:1em;display:inline-block;width:100%;margin:0;padding:11px 24px;text-align:center;text-decoration:none;border:0;border-radius:2px;outline:none;font-weight:300;cursor:pointer}button:hover,input[type='button']:hover,input[type='reset']:hover,input[type='submit']:hover,.button--primary:hover,.button--secondary:hover,.button--neutral:hover,.button--inline-neutral:hover,.button--positive:hover,.button--inline-positive:hover,.button--destructive:hover,.button--inline-destructive:hover,.button--base:hover,.button--inline-base:hover{text-decoration:none}@media only screen and (min-width: 768px){button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{width:auto}}.clearfix{display:block}.clearfix:after{clear:both;content:'\0020';display:block;height:0;overflow:hidden;visibility:hidden}textarea,input[type='text'],input[type='number'],input[type='search'],input[type='password'],input[type='email'],input[type='url'],input[type='tel'],select{-webkit-appearance:textfield;border-radius:2px;border:1px solid #cdcdcd;box-shadow:inset 0 1px 2px rgba(0,0,0,0.12);font-size:16px;margin:0;outline:none;padding:.6956522em .869565em;vertical-align:baseline;font-weight:300}textarea:active,input[type='text']:active,input[type='number']:active,input[type='search']:active,input[type='password']:active,input[type='email']:active,input[type='url']:active,input[type='tel']:active,select:active,textarea:focus,input[type='text']:focus,input[type='number']:focus,input[type='search']:focus,input[type='password']:focus,input[type='email']:focus,input[type='url']:focus,input[type='tel']:focus,select:focus{border-color:#888;outline:none}textarea::placeholder,input[type='text']::placeholder,input[type='number']::placeholder,input[type='search']::placeholder,input[type='password']::placeholder,input[type='email']::placeholder,input[type='url']::placeholder,input[type='tel']::placeholder,select::placeholder{color:contrast-friendly-search-color(#f7f7f7)}textarea[disabled="disabled"],input[disabled="disabled"][type='text'],input[disabled="disabled"][type='number'],input[disabled="disabled"][type='search'],input[disabled="disabled"][type='password'],input[disabled="disabled"][type='email'],input[disabled="disabled"][type='url'],input[disabled="disabled"][type='tel'],select[disabled="disabled"]{opacity:.5}input[type='checkbox'],input[type='radio']{border-radius:2px;border:1px solid #cdcdcd;box-shadow:inset 0 1px 2px rgba(0,0,0,0.12);height:14px;padding:0;vertical-align:middle;width:14px}.inner-wrapper{margin:0 auto;max-width:1030px}[class*='-col'].last-col,[class*='-col'] [class*='-col'].last-col{margin-right:0}.one-col,.two-col,.three-col,.four-col,.five-col,.six-col,.seven-col,.eight-col,.nine-col,.ten-col,.eleven-col,.twelve-col{clear:none;margin-bottom:20px;margin-right:0;padding:0;display:inline-block;position:relative;width:100%}@media only screen and (min-width: 768px){.one-col,.two-col,.three-col,.four-col,.five-col,.six-col,.seven-col,.eight-col,.nine-col,.ten-col,.eleven-col,.twelve-col{float:left}}.list-ticks,.list{list-style:none;margin-left:0}.list-ticks li,.list li{border-bottom:1px dotted #888;margin-bottom:0;padding:10px 0}.list-ticks li:last-of-type,.list li:last-of-type,.list-ticks .last-item,.list .last-item{border:0;margin-bottom:0;padding-bottom:0}.list-ticks li{background-repeat:no-repeat;background-position:0 1em;padding-left:25px}.box{background:#fff;border:1px solid #cdcdcd;padding:1.25em 20px}@media only screen and (min-width: 768px){.equal-height,.equal-height--vertical-divider{display:flex;flex-wrap:wrap;flex-direction:row;width:100%}.equal-height .equal-height__item,.equal-height--vertical-divider .equal-height__item{display:flex;flex:auto;flex-direction:column}}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{font-size:1em;box-sizing:border-box;width:100%;margin:0;padding:11px 24px;text-align:center;text-decoration:none;border-radius:2px;outline:none;font-weight:300;cursor:pointer;line-height:1em}button:disabled,input[type='button']:disabled,input[type='reset']:disabled,input[type='submit']:disabled,.button--primary:disabled,.button--secondary:disabled,.button--neutral:disabled,.button--inline-neutral:disabled,.button--positive:disabled,.button--inline-positive:disabled,.button--destructive:disabled,.button--inline-destructive:disabled,.button--base:disabled,.button--inline-base:disabled,button.button--disabled,input.button--disabled[type='button'],input.button--disabled[type='reset'],input.button--disabled[type='submit'],.button--disabled.button--primary,.button--disabled.button--secondary,.button--disabled.button--neutral,.button--disabled.button--inline-neutral,.button--disabled.button--positive,.button--disabled.button--inline-positive,.button--disabled.button--destructive,.button--disabled.button--inline-destructive,.button--disabled.button--base,.button--disabled.button--inline-base{opacity:.5;cursor:default}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary,.button--secondary,.button--neutral,.button--inline-neutral,.button--positive,.button--inline-positive,.button--destructive,.button--inline-destructive,.button--base,.button--inline-base{line-height:20px;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;user-select:none;padding:8px 14px;height:36px;box-sizing:border-box}button[disabled],input[disabled][type='button'],input[disabled][type='reset'],input[disabled][type='submit'],[disabled].button--primary,[disabled].button--secondary,[disabled].button--neutral,[disabled].button--inline-neutral,[disabled].button--positive,[disabled].button--inline-positive,[disabled].button--destructive,[disabled].button--inline-destructive,[disabled].button--base,[disabled].button--inline-base,button.button--disabled,input.button--disabled[type='button'],input.button--disabled[type='reset'],input.button--disabled[type='submit'],.button--disabled.button--primary,.button--disabled.button--secondary,.button--disabled.button--neutral,.button--disabled.button--inline-neutral,.button--disabled.button--positive,.button--disabled.button--inline-positive,.button--disabled.button--destructive,.button--disabled.button--inline-destructive,.button--disabled.button--base,.button--disabled.button--inline-base{pointer-events:none;opacity:.5}button.button--inline,input.button--inline[type='button'],input.button--inline[type='reset'],input.button--inline[type='submit'],.button--inline.button--primary,.button--inline.button--secondary,.button--inline.button--neutral,.button--inline.button--inline-neutral,.button--inline.button--positive,.button--inline.button--inline-positive,.button--inline.button--destructive,.button--inline.button--inline-destructive,.button--inline.button--base,.button--inline.button--inline-base{display:inline-block;width:auto}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,ol,ul,li,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;margin:0;padding:0;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,nav,section{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none}[hidden]{display:none}html{font-size:100%;overflow-y:scroll;text-size-adjust:100%}blockquote,q{quotes:none}legend{border:0}figure{margin:0}abbr,acronym{cursor:help}.link-arrow:after{content:'\0000a0›'}nav ul li h2 a:after{content:'\0000a0›'}nav ul li a:after,.carousel ul li a:after,ul li p a:after{content:''}svg:not(:root){overflow:hidden}img{border:0;height:auto;max-width:100%}img .left{margin-right:20px}img .right{margin-left:20px}.middle img{vertical-align:middle;margin-top:4em}ins{background:#fffbeb;text-decoration:none}.left{float:left}.right{float:right}.no-border{border:0}.link-top{font-size:.875em;clear:both;margin-bottom:40px;margin-top:-40px}.link-top a{background:#fff;margin-right:10px;margin-top:-35px;padding:5px;float:right}.caps{text-transform:uppercase}.touch-border,.touch-bottom{float:left}@media only screen and (min-width: 768px){.touch-border,.touch-bottom{margin-bottom:-20px}}@media only screen and (min-width: 1440px){.touch-border,.touch-bottom{margin-bottom:-40px}}.accessibility-aid,.off-left{position:absolute;left:-999em}.external{background-size:.7em .7em;padding-right:.9em;background-image:url("https://assets.ubuntu.com/v1/f24a8aa0-external-link-orange.svg");background-position:100% top;background-repeat:no-repeat}.no-svg .external{background-image:url("https://assets.ubuntu.com/v1/f24a8aa0-external-link-orange.svg")}.text-center,.align-center{text-align:center}.no-margin-bottom{margin-bottom:0}.no-padding-bottom{padding-bottom:0}.pull-bottom-right{float:right;margin-right:-10px}@media only screen and (min-width: 768px){.pull-bottom-right{margin-right:-40px;margin-bottom:-40px}}@media only screen and (min-width: 1440px){.pull-bottom-right{margin-right:-40px;margin-bottom:-60px}}.pull-bottom-left{float:left;margin-left:-10px}@media only screen and (min-width: 768px){.pull-bottom-left{margin-left:-40px;margin-bottom:-40px}}@media only screen and (min-width: 1440px){.pull-bottom-left{margin-left:-40px;margin-bottom:-60px}}@media only screen and (max-width: 767px){.priority-0,.not-for-small{display:none}}@media only screen and (min-width: 768px){.for-mobile,.for-small{display:none}}.video-container{position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden}.video-container iframe{position:absolute;top:0;left:0;width:100%;height:100%}.video-container+.video-title{margin-top:20px}.pull-right{float:right;margin-right:-40px}@media only screen and (max-width: 768px){.pull-right{margin-bottom:20px}}.pull-left{margin-left:-40px}.for-tablet,.for-medium{display:none}@media only screen and (min-width: 768px){.for-tablet,.for-medium{display:block}}@media only screen and (min-width: 768px) and (max-width: 1440px){.med-six-col .three-col{width:48.93617%}.med-six-col .three-col:nth-of-type(2n){margin-right:0}}blockquote{margin:0}blockquote>p{font-size:1em;margin:0 0 .4em}blockquote small{font-size:.8125em;line-height:1.4}.pull-quote{text-indent:0}.pull-quote p{font-size:1.5em;line-height:1.3;margin-left:.4em;padding-left:10px;padding-right:10px;text-indent:-.4em}.pull-quote p span{color:#f7f7f7;font-weight:bold;left:-5px;line-height:0;position:relative}.pull-quote p span:last-of-type{left:5px}.pull-quote p cite{display:block;font-size:.75em;font-weight:300;margin:10px 0 0;text-indent:0}@media only screen and (min-width: 768px){.pull-quote p{padding-left:0;padding-right:0;text-indent:-.7em}.pull-quote p span{top:5px;font-size:1.391304348em}.pull-quote p cite{margin-left:0;text-indent:0}}@media only screen and (min-width: 1440px){.pull-quote p span{top:10px}}.row-quote{border-radius:0}.row-quote blockquote{border-radius:4px;margin:0;padding:0}.row-quote blockquote p{color:#333;line-height:1.3;margin-bottom:.75em;padding-left:10px;padding-right:10px;text-indent:0}.row-quote blockquote span{color:#f7f7f7;font-weight:bold;left:-5px;line-height:0;position:relative}.row-quote blockquote span+span{left:5px}.row-quote blockquote cite{color:#333;font-size:.75em;font-style:normal;margin-bottom:0;text-indent:0}@media only screen and (min-width: 768px){.row-quote{text-indent:-.4em}.row-quote blockquote{text-indent:-7px}.row-quote blockquote p{font-size:1.5em;padding-left:0;padding-right:0;text-indent:-.4em}.row-quote blockquote span{top:5px;font-size:1.391304348em}.row-quote blockquote cite{margin-left:0;text-indent:0}}@media only screen and (min-width: 1440px){.row-quote blockquote{padding:0 80px 20px;text-indent:-10px}.row-quote blockquote p span{top:10px}}button,input[type='button'],input[type='reset'],input[type='submit'],.button--primary{color:#fff;background-color:#dd4814}button:hover,input[type='button']:hover,input[type='reset']:hover,input[type='submit']:hover,.button--primary:hover{background-color:#c03f11}.button--secondary{color:#dd4814;background-color:#fff;border:1px solid #cdcdcd}.button--secondary:hover{background-color:#efefef}@media only screen and (min-width: 620px){.nav-secondary{border:1px solid #d2d2d2;border-top:0}}.nav-secondary .breadcrumb{margin-bottom:0;margin-left:0;padding-top:2px;padding-bottom:3px}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb{float:left;margin-left:4px}}.nav-secondary .breadcrumb li{color:#fff;display:inline-block;margin:0;width:100%}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li{display:inline-block;width:auto}}.nav-secondary .breadcrumb li:first-of-type{border-bottom:1px solid #d2d2d2}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li:first-of-type{border-bottom:0}}.nav-secondary .breadcrumb li a{color:#888;font-size:1em;display:inline-block;width:100%;margin-right:0;padding:8px 10px;text-decoration:none}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li a{font-size:.875em}}@media only screen and (min-width: 620px){.nav-secondary .breadcrumb li .breadcrumb-link,.nav-secondary .breadcrumb li .breadcrumb-link--second-level,.nav-secondary .breadcrumb li .active{display:inline-block;float:none;width:auto}}.nav-secondary .breadcrumb li .breadcrumb-link:after,.nav-secondary .breadcrumb li .breadcrumb-link--second-level:after{content:'\203A';display:inline-block;height:5px;margin-left:8px;position:absolute;width:5px}.nav-secondary .second-level-nav,.nav-secondary .third-level-nav{display:none;overflow:hidden;width:100%;margin:0;padding-top:10px;padding-bottom:10px;border-bottom:1px solid #d2d2d2}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav,.nav-secondary .third-level-nav{display:inline;float:none;margin-bottom:0;padding:0;width:auto;border-bottom:0}}.nav-secondary .second-level-nav li,.nav-secondary .third-level-nav li{margin:0}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav li,.nav-secondary .third-level-nav li{float:none;width:auto}}.nav-secondary .second-level-nav li a,.nav-secondary .third-level-nav li a{font-size:.875em;display:block;width:100%;height:100%;padding:10px;color:#333}@media only screen and (max-width: 620px){.nav-secondary .second-level-nav li a.active,.nav-secondary .third-level-nav li a.active{font-weight:500}}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav li a,.nav-secondary .third-level-nav li a{padding:8px 10px 0}.nav-secondary .second-level-nav li a.active,.nav-secondary .third-level-nav li a.active{color:#f7f7f7}}@media only screen and (min-width: 620px){.nav-secondary .second-level-nav li{width:auto}.nav-secondary .second-level-nav li .active,.nav-secondary .second-level-nav li .breadcrumb-link--second-level{color:#888}.nav-secondary .second-level-nav li .third-level-nav li a{color:#333}.nav-secondary .second-level-nav li .third-level-nav li a:hover{color:#f7f7f7}.nav-secondary .second-level-nav li .third-level-nav li .active{color:#f7f7f7;float:none}.nav-secondary .second-level-nav li .third-level-nav li .active:after{content:''}}.third-level-nav li a:hover{color:#f7f7f7}.third-level-nav li .active{color:#f7f7f7;float:none}@media only screen and (max-width: 619px){#breadcrumbs:hover .second-level-nav,#breadcrumbs:hover .third-level-nav,#breadcrumbs:hover .active{border:0;display:block;overflow:visible;float:left;width:100%;position:relative;clear:both}#breadcrumbs:hover li{border:0;float:left}#breadcrumbs:hover .second-level-nav{border-top:1px solid #d2d2d2}#breadcrumbs:hover .second-level-nav li{float:left}#breadcrumbs:hover .third-level-nav{border-bottom:1px solid #d2d2d2;padding-top:0}#breadcrumbs:hover .third-level-nav li{width:50%;display:block;padding-left:10px}}label{cursor:pointer;display:block;margin-bottom:4px}label.has-error{color:#df382c}label.has-warning{color:#eca918}label.has-success{color:#38b44a}label.has-information{color:#19b6ee}input[type='reset']{display:none}input[type='search']{-webkit-appearance:none;border-radius:0}input[type='checkbox']+label,input[type='radio']+label{display:inline;margin-left:5px;vertical-align:middle;width:auto}input.has-error{border:1px solid #df382c}input.has-warning{border:1px solid #eca918}input.has-success{border:1px solid #38b44a}input.has-information{border:1px solid #19b6ee}form ul{margin-left:0;list-style:none}textarea{overflow:auto;vertical-align:top}textarea[readonly='readonly']{color:#888;cursor:default}textarea[readonly='readonly']:active,textarea[readonly='readonly']:focus{border-color:#888;outline:none}fieldset{background-color:#f7f7f7;background-position:-15px -15px;background-repeat:no-repeat;border-radius:4px;border:0;margin-bottom:8px;padding:15px 20px}@media only screen and (min-width: 1440px){fieldset{padding:15px 20px}}fieldset h3{border-bottom:1px dotted #cdcdcd;margin-bottom:9px;padding-bottom:10px}form input,form select,form textarea{width:100%}@media only screen and (min-width: 768px){.one-col{float:left;width:6.40351%;margin-right:2.10526%}.one-col .one-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.two-col{float:left;width:14.91228%;margin-right:2.10526%}.two-col .one-col{float:left;width:43.81443%;margin-right:12.37113%}.two-col .two-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.three-col{float:left;width:23.42105%;margin-right:2.10526%}.three-col .one-col{float:left;width:27.83505%;margin-right:8.24742%}.three-col .two-col{float:left;width:63.91753%;margin-right:8.24742%}.three-col .three-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.four-col{float:left;width:31.92982%;margin-right:2.10526%}.four-col .one-col{float:left;width:20.36082%;margin-right:6.18557%}.four-col .two-col{float:left;width:46.90722%;margin-right:6.18557%}.four-col .three-col{float:left;width:73.45361%;margin-right:6.18557%}.four-col .four-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.five-col{float:left;width:40.4386%;margin-right:2.10526%}.five-col .one-col{float:left;width:16.04124%;margin-right:4.94845%}.five-col .two-col{float:left;width:37.03093%;margin-right:4.94845%}.five-col .three-col{float:left;width:58.02062%;margin-right:4.94845%}.five-col .four-col{float:left;width:79.01031%;margin-right:4.94845%}.five-col .five-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.six-col{float:left;width:48.94737%;margin-right:2.10526%}.six-col .one-col{float:left;width:13.23024%;margin-right:4.12371%}.six-col .two-col{float:left;width:30.58419%;margin-right:4.12371%}.six-col .three-col{float:left;width:47.93814%;margin-right:4.12371%}.six-col .four-col{float:left;width:65.2921%;margin-right:4.12371%}.six-col .five-col{float:left;width:82.64605%;margin-right:4.12371%}.six-col .six-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.seven-col{float:left;width:57.45614%;margin-right:2.10526%}.seven-col .one-col{float:left;width:11.25605%;margin-right:3.53461%}.seven-col .two-col{float:left;width:26.04671%;margin-right:3.53461%}.seven-col .three-col{float:left;width:40.83737%;margin-right:3.53461%}.seven-col .four-col{float:left;width:55.62802%;margin-right:3.53461%}.seven-col .five-col{float:left;width:70.41868%;margin-right:3.53461%}.seven-col .six-col{float:left;width:85.20934%;margin-right:3.53461%}.seven-col .seven-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.eight-col{float:left;width:65.96491%;margin-right:2.10526%}.eight-col .one-col{float:left;width:9.79381%;margin-right:3.09278%}.eight-col .two-col{float:left;width:22.68041%;margin-right:3.09278%}.eight-col .three-col{float:left;width:35.56701%;margin-right:3.09278%}.eight-col .four-col{float:left;width:48.45361%;margin-right:3.09278%}.eight-col .five-col{float:left;width:61.34021%;margin-right:3.09278%}.eight-col .six-col{float:left;width:74.2268%;margin-right:3.09278%}.eight-col .seven-col{float:left;width:87.1134%;margin-right:3.09278%}.eight-col .eight-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.nine-col{float:left;width:74.47368%;margin-right:2.10526%}.nine-col .one-col{float:left;width:8.66743%;margin-right:2.74914%}.nine-col .two-col{float:left;width:20.084%;margin-right:2.74914%}.nine-col .three-col{float:left;width:31.50057%;margin-right:2.74914%}.nine-col .four-col{float:left;width:42.91714%;margin-right:2.74914%}.nine-col .five-col{float:left;width:54.33372%;margin-right:2.74914%}.nine-col .six-col{float:left;width:65.75029%;margin-right:2.74914%}.nine-col .seven-col{float:left;width:77.16686%;margin-right:2.74914%}.nine-col .eight-col{float:left;width:88.58343%;margin-right:2.74914%}.nine-col .nine-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.ten-col{float:left;width:82.98246%;margin-right:2.10526%}.ten-col .one-col{float:left;width:7.7732%;margin-right:2.47423%}.ten-col .two-col{float:left;width:18.02062%;margin-right:2.47423%}.ten-col .three-col{float:left;width:28.26804%;margin-right:2.47423%}.ten-col .four-col{float:left;width:38.51546%;margin-right:2.47423%}.ten-col .five-col{float:left;width:48.76289%;margin-right:2.47423%}.ten-col .six-col{float:left;width:59.01031%;margin-right:2.47423%}.ten-col .seven-col{float:left;width:69.25773%;margin-right:2.47423%}.ten-col .eight-col{float:left;width:79.50515%;margin-right:2.47423%}.ten-col .nine-col{float:left;width:89.75258%;margin-right:2.47423%}.ten-col .ten-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.eleven-col{float:left;width:91.49123%;margin-right:2.10526%}.eleven-col .one-col{float:left;width:7.04609%;margin-right:2.2493%}.eleven-col .two-col{float:left;width:16.34148%;margin-right:2.2493%}.eleven-col .three-col{float:left;width:25.63687%;margin-right:2.2493%}.eleven-col .four-col{float:left;width:34.93227%;margin-right:2.2493%}.eleven-col .five-col{float:left;width:44.22766%;margin-right:2.2493%}.eleven-col .six-col{float:left;width:53.52305%;margin-right:2.2493%}.eleven-col .seven-col{float:left;width:62.81844%;margin-right:2.2493%}.eleven-col .eight-col{float:left;width:72.11383%;margin-right:2.2493%}.eleven-col .nine-col{float:left;width:81.40922%;margin-right:2.2493%}.eleven-col .ten-col{float:left;width:90.70461%;margin-right:2.2493%}.eleven-col .eleven-col{width:100%;margin-right:0}}@media only screen and (min-width: 768px){.prepend-one{margin-left:8.50877%}.prepend-two{margin-left:17.01754%}.prepend-three{margin-left:25.52632%}.prepend-four{margin-left:34.03509%}.prepend-five{margin-left:42.54386%}.prepend-six{margin-left:51.05263%}.prepend-seven{margin-left:59.5614%}.prepend-eight{margin-left:68.07018%}.prepend-nine{margin-left:76.57895%}.prepend-ten{margin-left:85.08772%}.prepend-eleven{margin-left:93.59649%}}@media only screen and (min-width: 768px){.append-one{margin-right:8.50877%}.append-two{margin-right:17.01754%}.append-three{margin-right:25.52632%}.append-four{margin-right:34.03509%}.append-five{margin-right:42.54386%}.append-six{margin-right:51.05263%}.append-seven{margin-right:59.5614%}.append-eight{margin-right:68.07018%}.append-nine{margin-right:76.57895%}.append-ten{margin-right:85.08772%}.append-eleven{margin-right:93.59649%}}@media only screen and (min-width: 768px){.push-one{float:left;position:relative;margin:0 -8.50877% 0 8.50877%}.push-two{float:left;position:relative;margin:0 -19.12281% 0 19.12281%}.push-three{float:left;position:relative;margin:0 -27.63158% 0 27.63158%}.push-four{float:left;position:relative;margin:0 -36.14035% 0 36.14035%}.push-five{float:left;position:relative;margin:0 -44.64912% 0 44.64912%}.push-six{float:left;position:relative;margin:0 -53.15789% 0 53.15789%}.push-seven{float:left;position:relative;margin:0 -61.66667% 0 61.66667%}.push-eight{float:left;position:relative;margin:0 -70.17544% 0 70.17544%}.push-nine{float:left;position:relative;margin:0 -78.68421% 0 78.68421%}.push-ten{float:left;position:relative;margin:0 -87.19298% 0 87.19298%}.push-eleven{float:left;position:relative;margin:0 -95.70175% 0 95.70175%}}@media only screen and (min-width: 768px){.pull-one{float:left;position:relative;margin-left:-6.40351%}.pull-two{float:left;position:relative;margin-left:17.01754%}.pull-three{float:left;position:relative;margin-left:25.52632%}.pull-four{float:left;position:relative;margin-left:34.03509%}.pull-five{float:left;position:relative;margin-left:42.54386%}.pull-six{float:left;position:relative;margin-left:51.05263%}.pull-seven{float:left;position:relative;margin-left:59.5614%}.pull-eight{float:left;position:relative;margin-left:68.07018%}.pull-nine{float:left;position:relative;margin-left:76.57895%}.pull-ten{float:left;position:relative;margin-left:85.08772%}.pull-eleven{float:left;position:relative;margin-left:93.59649%}}.banner{background:#f7f7f7;border-bottom:0;border-top:0;box-shadow:inset 0 -1px 0 #ccc;display:block;min-width:100%;position:relative;width:auto;z-index:2}@media only screen and (min-width: 620px){.banner{border-bottom:1px solid #ccc;box-shadow:none}}.banner .logo{position:relative;display:table;float:left;overflow:hidden;height:48px;margin:0 10px;border-left:0}@media only screen and (min-width: 620px){.banner .logo{margin:0 15px}}.banner .logo a{display:table-cell;height:100%;vertical-align:middle;color:#fff}.banner .logo a span{display:inline-block}.banner h2{font-size:1.563em;position:relative;top:14px;left:4px;display:block;margin-bottom:0;text-transform:lowercase}.banner h2 a,.banner h2 a:link,.banner h2 a:visited{float:left;text-decoration:none;color:#fff}.banner .nav-primary{overflow:hidden;margin:0 auto;border:0}@media only screen and (min-width: 620px){.banner .nav-primary{max-width:1440px}}.banner .nav-primary span{display:none}.banner .nav-primary ul{display:none;float:left;width:100%;margin:0;padding:0;border-top:1px solid #ccc;box-shadow:inset 0 -1px 0 #ccc;background-color:#ebebeb}@media only screen and (min-width: 620px){.banner .nav-primary ul{position:static;display:block;width:auto;border-top:0;background-color:transparent;box-shadow:none}}.banner .nav-primary ul li{float:left;width:50%;margin:0;border-right:1px solid #ccc;border-bottom:1px solid #ccc;background:transparent}@media only screen and (min-width: 620px){.banner .nav-primary ul li{width:auto;border-right:0;border-bottom:0;border-left:1px solid #e3e3e3}.banner .nav-primary ul li:last-child{border-right:1px solid #e3e3e3}.banner .nav-primary ul li:last-child a{border-right:0}}@media only screen and (max-width: 620px){.banner .nav-primary ul li:nth-child(2n+2){border-right:0}}.banner .nav-primary ul li a,.banner .nav-primary ul li a:link,.banner .nav-primary ul li a:visited{font-size:1em;font-weight:300;position:relative;display:block;margin-bottom:0;padding:8px 10px;text-align:left;text-decoration:none;background-color:#ebebeb;font-smoothing:subpixel-antialiased;color:#333}@media only screen and (min-width: 620px){.banner .nav-primary ul li a,.banner .nav-primary ul li a:link,.banner .nav-primary ul li a:visited{font-size:.875em;padding:14px 14px 13px;text-align:center;border-left:1px solid #fff;background-color:transparent;color:#fff}}.banner .nav-primary ul li a:hover,.banner .nav-primary ul li a:link:hover,.banner .nav-primary ul li a:visited:hover{background:#f2f2f2}@media only screen and (min-width: 620px){.banner .nav-primary ul li a:hover,.banner .nav-primary ul li a:link:hover,.banner .nav-primary ul li a:visited:hover{background-color:#fff}}.banner .nav-primary ul li a.active,.banner .nav-primary ul li a:link.active,.banner .nav-primary ul li a:visited.active{background-color:#ddd}@media only screen and (min-width: 620px){.banner .nav-primary ul li a.active,.banner .nav-primary ul li a:link.active,.banner .nav-primary ul li a:visited.active{background-color:#e3e3e3;border-left:0}}.banner .nav-toggle{position:absolute;top:0;right:0;display:block;width:48px;height:48px;cursor:pointer;text-indent:-99999px;background-image:url("https://assets.ubuntu.com/v1/12387180-navigation-menu-plain.svg");background-repeat:no-repeat;background-position:center center;background-size:25px auto}@media only screen and (min-width: 620px){.banner .nav-toggle{display:none}}.banner .nav-toggle .nav-toggle__link{width:48px;height:48px}.banner .nav-toggle .nav-toggle__link.open{display:block}.banner .nav-toggle .nav-toggle__link.close{display:none}@media only screen and (max-width: 620px){.banner #nav:hover ul{display:block}.banner #nav:hover ul ul{display:none}.banner #nav:hover .nav-toggle .nav-toggle__link.open{display:none}.banner #nav:hover .nav-toggle .nav-toggle__link.close{display:block}}ol,ul{margin-left:20px;margin-bottom:20px}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}nav ul,nav ol{list-style:none;list-style-image:none}li{font-size:1em;line-height:1.5;margin:0 0 .4em;padding:0}.list-ticks li{background-image:url("data:image/svg+xml;utf8, ")}.no-bullets{list-style:none;margin-left:0}.combined-list ul,.combined-list div{margin-bottom:0}.combined-list .last-item{border-bottom:1px dotted #888;padding-bottom:10px}.combined-list .last-col{margin-bottom:20px}.combined-list .last-col .last-item{border-bottom:0;padding-bottom:0}@media only screen and (min-width: 768px){.combined-list ul,.combined-list div{margin-bottom:20px}.combined-list .last-item{border-bottom:0;padding-bottom:0}}.inline-list{margin-left:0}.inline-list li{display:inline;list-style:none;margin-right:20px}.inline-list .last-item{margin-right:0}.list-step{list-style:none;margin-left:60px}@media only screen and (max-width: 1440px){.list-step__item:first-child{margin-top:10px}}@media only screen and (min-width: 1440px){.list-step__title{margin-bottom:0}}.list-step__bullet{box-shadow:0 1px 3px 1px rgba(51,51,51,0.2);background:#fff;border-radius:50%;padding:.3em .68em;display:inline-block;color:#f7f7f7;margin-right:.34375em;margin-bottom:.625em;margin-left:-60px}@media only screen and (max-width: 1440px){.list-step__bullet{position:absolute;top:-5px}}.row{border-bottom:1px dotted #888;clear:both;padding:20px 10px 0;position:relative}.row br{display:none}.row.no-padding-bottom{padding-bottom:0}.row::after{clear:both;content:'.';display:block;height:0;visibility:hidden}.row--light{background:#fff}.row--dark{background:#333;color:#fff}.row.row-grey,.row.row--grey{background:#f7f7f7;border:0;margin-top:-1px}.no-border{border:0}.row-hero{margin-top:20px;padding-top:0}.strip{width:100%;display:block}.strip-inner-wrapper{max-width:1440px;margin:auto}@media only screen and (min-width: 768px){.row-hero{margin-top:40px}.row{border-radius:0;margin:0;padding:40px 20px 20px}.row-grey{margin-top:-1px}}@media only screen and (min-width: 769px){.row br{display:block}}@media only screen and (min-width: 1440px){.row br{display:block}.row{padding:60px 20px 40px}.no-border{border:0}}.header-search [type="search"],.header-search [type="text"],.search-form [type="search"],.search-form [type="text"]{-webkit-appearance:none;background-color:#dedede;box-shadow:none;color:#333;display:block;float:left;font-size:1em;margin-bottom:0;padding:12px 10px;transition:all .5s ease-out;width:100%}.header-search .svg-search-handle,.search-form .svg-search-handle{fill:#333}.header-search .svg-search-frame,.search-form .svg-search-frame{stroke:#333}.header-search placeholder,.search-form placeholder{color:#333;opacity:1}.header-search input:placeholder,.search-form input:placeholder{color:#333;opacity:1}.header-search ::placeholder,.search-form ::placeholder{color:#333;opacity:1}.header-search [type="search"]:focus,.search-form [type="search"]:focus{background-color:#d3d3d3;border-color:#b3b3b3}.header-search [type=submit],.search-form [type=submit]{background:none;display:block;float:left;line-height:0;margin-left:-40px;overflow:visible;padding:3px 2px;width:auto}.header-search [type=submit]:hover,.search-form [type=submit]:hover{background:none}.header-search [type=submit] img,.search-form [type=submit] img{height:28px;width:28px;margin-top:2px}.banner .search-toggle{background-image:url("data:image/svg+xml;utf8, ");background-position:center center;background-repeat:no-repeat;background-size:20px 20px;display:block;height:48px;outline:none;overflow:hidden;position:absolute;right:0;text-indent:-999em;top:0;width:24px}.banner .search-toggle .search-toggle__link{width:48px;height:48px}.banner .search-toggle .search-toggle__link.open{display:block}.banner .search-toggle .search-toggle__link.close{display:none}#site-search:hover form{display:block}#site-search:hover .search-toggle .search-toggle__link.open{display:none}#site-search:hover .search-toggle .search-toggle__link.close{display:block}.header-search,.search-form{background:#f7f7f7;border:0;display:none;float:left;position:relative;margin:0;width:100%;z-index:3}.search-form.active,.header-search.active,.header-search.open{display:block}.search-form div,.header-search div{box-shadow:inset 0 -4px 4px -4px rgba(0,0,0,0.3),inset 0 5px 5px -5px rgba(0,0,0,0.3);background:#fff;margin:10px;position:relative;z-index:1}.search-form form [type="search"],.header-search form [type="search"]{background:#fff;border:0;box-shadow:0 2px 2px rgba(0,0,0,0.3) inset,0 -1px 3px rgba(0,0,0,0.2) inset,0 2px 0 rgba(255,255,255,0.4);color:#333;display:block;float:left;font-size:1em;height:auto;margin:0;padding:8px 10px;width:100%}.yes-js .header-inner .search-form,.yes-js .header-inner .header-search{display:none}.yes-js .header-inner .search-form form,.yes-js .header-inner .header-search form{margin-left:0;margin-right:0;overflow:hidden;padding:10px;position:relative;top:0;width:100%;z-index:999}@media only screen and (max-width: 620px){.banner .search-toggle{right:0}.no-svg .search-toggle,.opera-mini .search-toggle{background-image:url("https://assets.ubuntu.com/v1/75d8151d-search-white.png")}}@media only screen and (min-width: 620px){.banner .search-toggle{display:none}}@media only screen and (min-width: 620px){.search-form,.header-search{background:none;border-right:0 none;float:right;margin-bottom:0;max-width:220px;overflow:hidden;padding:7px 0 5px 14px}.search-form form [type="text"],.search-form form [type="search"],.header-search form [type="text"],.header-search form [type="search"]{box-shadow:0 2px 4px rgba(0,0,0,0.4) inset;box-sizing:content-box;background-color:#dedede;border:0 solid #d8d8d8;border-width:0 0 1px;color:#333;font-size:.813em;height:24px;margin-bottom:0;padding:.5em 2.5em .5em .5em;transition:all .5s ease 0s;width:86px}.search-form form [type="text"]:focus,.search-form form [type="search"]:focus,.header-search form [type="text"]:focus,.header-search form [type="search"]:focus{background-color:#d3d3d3;border-color:#c4c4c4}.search-form .svg-search-handle,.header-search .svg-search-handle{fill:#333}.search-form .svg-search-frame,.header-search .svg-search-frame{stroke:#333}.search-form placeholder,.header-search placeholder{color:#333}.search-form input:placeholder,.header-search input:placeholder{color:#333}.search-form ::placeholder,.header-search ::placeholder{color:#333}}@media only screen and (min-width: 620px){.header-search .svg-search-handle,.search-form .svg-search-handle{fill:#333}.header-search .svg-search-frame,.search-form .svg-search-frame{stroke:#333}.header-search placeholder,.search-form placeholder{color:#333}.header-search input:placeholder,.search-form input:placeholder{color:#333}.header-search ::placeholder,.search-form ::placeholder{color:#333}}@media only screen and (max-width: 620px){.banner .nav-primary .header-search{position:relative;top:0;width:100%}.banner .nav-primary .header-search [type="search"]{background-color:#ebebeb;color:#333}.banner .nav-primary .header-search [type="submit"]{background-color:transparent;height:38px;margin-top:0;width:32px}.banner .nav-primary .header-search [type="submit"] img,.banner .nav-primary .header-search [type="submit"] svg{max-width:none}.banner .nav-primary .header-search.open{display:block}.banner .search-toggle{background-image:url("data:image/svg+xml;utf8, ");background-position:center center;background-repeat:no-repeat;background-size:25px auto;cursor:pointer;display:block;height:48px;position:absolute;right:0;text-indent:-99999px;width:48px}.no-svg .banner .search-toggle,.opera-mini .banner .search-toggle{background-image:url("https://assets.ubuntu.com/v1/2196e362-search-white.svg")}.opera-mini x:-o-prefocus,.opera-mini .banner .search-toggle{background-size:25px auto}}@media only screen and (min-width: 620px){.search-form,.header-search{display:block}.search-form .svg-search-handle,.header-search .svg-search-handle{fill:#333}.search-form .svg-search-frame,.header-search .svg-search-frame{stroke:#333}.search-form form [type="text"]:focus,.header-search form [type="text"]:focus{width:160px}.search-form [type="search"],.search-form [type="text"],.header-search [type="search"],.header-search [type="text"]{padding:8px 10px}}@media only screen and (max-width: 1440px){.search-form,.header-search{margin-right:10px}}@media only screen and (max-width: 620px){.banner .search-toggle{right:48px}}.ubuntu-search .nav-secondary,.search-results .nav-secondary,.search-no-results .nav-secondary{display:none}.ubuntu-search section>h1,.ubuntu-search section article h1,.search-results section>h1,.search-results section article h1,.search-no-results section>h1,.search-no-results section article h1{padding-bottom:10px;font-size:1.438em;margin-bottom:0}.ubuntu-search section>h1,.search-results section>h1,.search-no-results section>h1{border-bottom:1px dotted #cdcdcd}.ubuntu-search .main-search,.search-results .main-search,.search-no-results .main-search{background-color:transparent;margin:0 0 20px;padding:20px 0}.ubuntu-search .main-search [type="search"],.search-results .main-search [type="search"],.search-no-results .main-search [type="search"]{border:1px solid #888;float:left;font-size:2em;padding:.2em 65px .2em .2em;width:100%}.ubuntu-search .main-search [type=submit],.search-results .main-search [type=submit],.search-no-results .main-search [type=submit]{width:32px;height:38px;background-repeat:no-repeat;background-position:center 8px;background-color:transparent;background-size:28px 28px;display:block;float:left;line-height:0;margin-left:-53px;margin-top:-4px;overflow:visible;padding:4px}.ubuntu-search .main-search [type=submit] img,.search-results .main-search [type=submit] img,.search-no-results .main-search [type=submit] img{height:45px;width:45px}.ubuntu-search .main-search [type=submit]:hover,.search-results .main-search [type=submit]:hover,.search-no-results .main-search [type=submit]:hover{background:none}.ubuntu-search .search-result h1 .title-main,.search-results .search-result h1 .title-main,.search-no-results .search-result h1 .title-main{margin-right:20px}.ubuntu-search .search-result h1 .result-url,.search-results .search-result h1 .result-url,.search-no-results .search-result h1 .result-url{color:#888;display:block;overflow:hidden;padding-bottom:2px;text-overflow:ellipsis;vertical-align:bottom}.ubuntu-search .search-result h1 .result-url a,.search-results .search-result h1 .result-url a,.search-no-results .search-result h1 .result-url a{color:#888}.ubuntu-search .search-result p,.search-results .search-result p,.search-no-results .search-result p{margin-bottom:0}.ubuntu-search .num-results,.search-results .num-results,.search-no-results .num-results{display:inline-block;margin-left:20px}.ubuntu-search .bottom-results-total,.search-results .bottom-results-total,.search-no-results .bottom-results-total{margin:0;overflow:visible;padding-top:20px;text-align:center;width:100%}.ubuntu-search .bottom-nav,.search-results .bottom-nav,.search-no-results .bottom-nav{margin-top:-26px;overflow:hidden}.ubuntu-search .bottom-nav ul,.search-results .bottom-nav ul,.search-no-results .bottom-nav ul{margin-bottom:0;margin-left:0;overflow:hidden;padding:0}.ubuntu-search .bottom-nav li,.search-results .bottom-nav li,.search-no-results .bottom-nav li{float:left;margin-left:15px}.ubuntu-search .bottom-nav li:first-child,.search-results .bottom-nav li:first-child,.search-no-results .bottom-nav li:first-child{margin-left:0}.ubuntu-search .nav-back,.search-results .nav-back,.search-no-results .nav-back{float:left}.ubuntu-search .nav-back li:before,.search-results .nav-back li:before,.search-no-results .nav-back li:before{color:#f7f7f7;content:'\2039';margin-right:5px}.ubuntu-search .nav-back .item-extreme:before,.search-results .nav-back .item-extreme:before,.search-no-results .nav-back .item-extreme:before{content:'\2039\2039'}.ubuntu-search .nav-forward,.search-results .nav-forward,.search-no-results .nav-forward{float:right}.ubuntu-search .nav-forward li:after,.search-results .nav-forward li:after,.search-no-results .nav-forward li:after{color:#f7f7f7;content:'\203A';margin-left:5px}.ubuntu-search .nav-forward .item-extreme:after,.search-results .nav-forward .item-extreme:after,.search-no-results .nav-forward .item-extreme:after{content:'\203A\203A'}.ubuntu-search .error-notification,.search-results .error-notification,.search-no-results .error-notification{background-color:#fff;color:#333;display:block;margin-top:20px;padding:20px;width:100%}.ubuntu-search .result-line,.search-results .result-line,.search-no-results .result-line{color:#888}.ubuntu-search .results-top,.search-results .results-top,.search-no-results .results-top{border-bottom:1px dotted #cdcdcd;padding-bottom:.5em}.ubuntu-search .search-container,.search-results .search-container,.search-no-results .search-container{padding-bottom:0}@media only screen and (min-width: 620px){.ubuntu-search .main-search [type=submit]{margin-left:-60px;margin-top:0}}@font-face{font-family:'Ubuntu';font-style:normal;font-weight:300;src:url("../assets/fonts/ubuntu-l-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-l-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:normal;font-weight:400;src:url("../assets/fonts/ubuntu-r-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-r-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:normal;font-weight:700;src:url("../assets/fonts/ubuntu-b-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-b-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:italic;font-weight:300;src:url("../assets/fonts/ubuntu-li-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-li-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:italic;font-weight:400;src:url("../assets/fonts/ubuntu-ri-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-ri-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu';font-style:italic;font-weight:700;src:url("../assets/fonts/ubuntu-bi-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntu-bi-webfont.woff") format("woff")}@font-face{font-family:'Ubuntu Mono';font-style:normal;font-weight:400;src:url("../assets/fonts/ubuntumono-r-webfont.woff2") format("woff2"),url("../assets/fonts/ubuntumono-r-webfont.woff") format("woff")}body{color:#333;font-family:'Ubuntu', Arial, 'libra sans', sans-serif;font-size:16px;font-weight:300}a:focus{outline:thin dotted}a:hover,a:active{outline:0}a{color:#f7f7f7;text-decoration:none}a:hover,a:active,a:focus{text-decoration:underline}strong{font-weight:400}.caps-centered,.muted-heading{font-size:.875em;margin-bottom:20px;text-align:center;text-transform:uppercase}small,.smaller{font-size:13px}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{vertical-align:text-top}sub{vertical-align:text-bottom}pre{border-radius:4px;background-color:#fff;padding:.6em 1em;white-space:pre-wrap;word-wrap:break-word}p+h2,ul+h2,ol+h2,pre+h2{margin-top:0.5625em}header nav a:link{font-weight:normal}p+h3,ul+h3,ol+h3,pre+h3{margin-top:.783em}p+h4,ul+h4,ol+h4,pre+h4{margin-top:1.21875}ol+h2,p+h2,pre+h2,ul+h2{margin-top:.563em}ol+h3,p+h3,pre+h3,ul+h3{margin-top:.783em}ol+h4,p+h4,pre+h4,ul+h4{margin-top:1.219em}.intro{font-size:1em;line-height:1.4}.row div p:last-child,.row ul p:last-child{margin-bottom:0}.four-col p:last-child{margin-bottom:0}.note{color:#888;font-size:.813em}h1,h2,h3,h4,h5,h6{font-family:Ubuntu, Arial, 'libra sans', sans-serif;font-weight:300;line-height:1.3}h1{font-size:2.8125em;margin-bottom:.5em}h2{font-size:2em;margin-bottom:.5em}h3{font-size:1.4375em;margin-bottom:.522em}h4{font-size:1.25em;font-weight:400;margin-bottom:.615em}h5{font-size:1em;font-weight:700;margin-bottom:1em}h6{font-size:.8125em;font-weight:400;margin-bottom:1em;letter-spacing:.1em;text-transform:uppercase}p,li{font-size:1em;line-height:1.5;margin:0;margin-bottom:.75em;padding:0}button,input,select,textarea{font-family:Ubuntu,Arial,'libra sans',sans-serif}@media only screen and (min-width: 768px) and (max-width: 1440px){h1{font-size:1.5234375em;margin-bottom:.5em}h2{font-size:1.348125em;margin-bottom:.5em}h3{font-size:1.1428125em;margin-bottom:.522em}h4{font-size:1.171875em;font-weight:400;margin-bottom:.615em}h5{font-size:.9375em;font-weight:700;margin-bottom:1em}h6{font-size:.6778125em;font-weight:400;margin-bottom:1em;letter-spacing:.1em;text-transform:uppercase}.intro{font-size:1.13333em}}@media only screen and (max-width: 768px){h1{font-size:1.421875em;margin-bottom:.4375em}h2{font-size:1.25825em;margin-bottom:.4375em}h3{font-size:1.066625em;margin-bottom:.45675em}h4{font-size:1.09375em;font-weight:400;margin-bottom:.538125em}h5{font-size:.875em;font-weight:700;margin-bottom:.875em}h6{font-size:.632625em;font-weight:400;margin-bottom:.875em;letter-spacing:.1em;text-transform:uppercase}p,li{font-size:.875em}}@media only screen and (min-width: 1440px){p,li,code,pre{font-size:16px;line-height:1.5;margin-bottom:.75em}.intro{font-size:1.25em}}dfn{font-style:italic}code,pre{font-family:'Ubuntu Mono', 'Consolas', 'Monaco', 'Lucida Console', 'Courier New', Courier, monospace}table{border-collapse:collapse;border-spacing:0;overflow-x:scroll;margin-bottom:20px;margin:0 0 2.5em;width:100%}table th,table td{background:#f7f7f7;border:1px dotted #888;padding:15px 10px}table td{text-align:center;vertical-align:middle}table thead th{border-collapse:separate;border-spacing:0 10px;background:#fee3d2;color:#333;font-weight:normal}table tbody th{font-weight:300;text-align:left}table th[scope='col']{text-align:center}table thead th:first-of-type{text-align:left}@media only screen and (max-width: 768px){table{display:block}}body footer.global #nav-global li:first-of-type a{margin-left:0}footer.global{background:none;border-top:0;box-shadow:inset 0 2px 2px -1px #cdcdcd;clear:both;display:block;padding:30px 10px 20px;position:relative;width:100%}footer.global .legal{background-image:none;clear:both;margin:0 auto;min-height:40px;position:relative;width:100%}footer.global .legal p,footer.global .legal ul{padding-left:0}footer.global h2{font-size:.75em;line-height:1.4;margin-bottom:0;padding-bottom:.5em}footer.global h2,footer.global h2 a:link,footer.global h2 a:visited{color:#333;font-weight:normal}footer.global ul{margin:0}footer.global li a:hover{color:#f7f7f7}footer.global li ul li a:link,footer.global li ul li a:visited{color:#333;font-size:.75em}footer.global h2 a:hover,footer.global h2 a:active{color:#f7f7f7}footer.global p{color:#333;font-size:12px;margin-bottom:0}@media only screen and (max-width: 768px){footer.no-global .legal{box-shadow:0 2px 2px -1px #cdcdcd inset;box-sizing:content-box;margin-left:-10px;padding-left:10px;padding-right:10px;padding-top:10px}}@media only screen and (min-width: 1440px){footer.global .legal{width:1440px}footer.global{padding:30px 0 20px}}svg:not(:root){overflow:hidden}figure{width:100%}figure caption{display:block;width:100%;text-align:center}object,iframe,embed,canvas,video,audio{display:block;max-width:100%;margin:0 auto 20px}audio:not([controls]){display:none;height:0}[hidden]{display:none}.video-container{position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden}.video-container iframe{position:absolute;top:0;left:0;width:100%;height:100%}.video-container h3,.video-container .video-title{margin-top:20px}.inline-logos{float:left;margin-left:0;padding:0;text-align:center;width:100%}.inline-logos .inline-logos__item{display:inline-block;margin-right:20px;padding:0 0 20px;width:36%}@media only screen and (max-width: 300px){.inline-logos .inline-logos__item{margin:0 0 20px;width:100%}}.inline-logos .inline-logos__item.clear-row{clear:left}.inline-logos .inline-logos__item.last-item{border:0}.inline-logos .inline-logos__image{max-height:32px;max-width:115px;transition:all .3s ease-out;vertical-align:middle}@media only screen and (min-width: 768px){.inline-logos{padding-top:20px}.inline-logos .inline-logos__item{display:inline-block;height:56px;line-height:60px;margin:0 30px 30px;width:150px}.inline-logos .inline-logos__image{float:none;height:auto;max-height:56px;max-width:150px;vertical-align:middle}}@media only screen and (min-width: 1440px){.inline-logos__item{margin-bottom:40px}}@media only screen and (min-width: 768px){.equal-height__align-vertically{align-items:center;justify-content:center}}@media only screen and (min-width: 768px){.equal-height--vertical-divider{position:relative}.equal-height--vertical-divider__item{padding-left:10px;padding-right:10px}.equal-height--vertical-divider__item:before{content:'';position:absolute;right:-10px;top:0;height:100%;width:1px;border-right:1px dotted #888}.equal-height--vertical-divider .last-col,.equal-height--vertical-divider__item:last-of-type{padding-right:0}.equal-height--vertical-divider .last-col:before,.equal-height--vertical-divider__item:last-of-type:before{border-right:0}.equal-height--vertical-divider__item:first-of-type{padding-left:0}}@media only screen and (max-width: 768px){.equal-height--vertical-divider .equal-height--vertical-divider__item{border-bottom:1px dotted #888;padding-bottom:20px}.equal-height--vertical-divider .equal-height--vertical-divider__item:last-of-type{border-bottom:0;padding-bottom:inherit}}body{background:url("../assets/img/backgrounds/image-background-paper.png") repeat-y center top #f7f7f7}.button--neutral,.button--inline-neutral{color:#333;background-color:transparent;border:1px solid #cdcdcd}.button--neutral:focus,.button--neutral:active,.button--neutral:hover,.button--inline-neutral:focus,.button--inline-neutral:active,.button--inline-neutral:hover{background-color:rgba(0,0,0,0.1)}.button--neutral:disabled:focus,.button--neutral:disabled:active,.button--neutral:disabled:hover,.button--neutral.button--disabled:focus,.button--neutral.button--disabled:active,.button--neutral.button--disabled:hover,.button--inline-neutral:disabled:focus,.button--inline-neutral:disabled:active,.button--inline-neutral:disabled:hover,.button--inline-neutral.button--disabled:focus,.button--inline-neutral.button--disabled:active,.button--inline-neutral.button--disabled:hover{background-color:transparent}.button--inline-neutral{display:inline-block;width:auto}.button--positive,.button--inline-positive{color:#fff;background-color:#38b44a}.button--positive:focus,.button--positive:active,.button--positive:hover,.button--inline-positive:focus,.button--inline-positive:active,.button--inline-positive:hover{background-color:#2c8d3a}.button--positive:disabled:focus,.button--positive:disabled:active,.button--positive:disabled:hover,.button--positive.button--disabled:focus,.button--positive.button--disabled:active,.button--positive.button--disabled:hover,.button--inline-positive:disabled:focus,.button--inline-positive:disabled:active,.button--inline-positive:disabled:hover,.button--inline-positive.button--disabled:focus,.button--inline-positive.button--disabled:active,.button--inline-positive.button--disabled:hover{background-color:#38b44a}.button--inline-positive{display:inline-block;width:auto}.button--destructive,.button--inline-destructive{color:#fff;background-color:#df382c}.button--destructive:focus,.button--destructive:active,.button--destructive:hover,.button--inline-destructive:focus,.button--inline-destructive:active,.button--inline-destructive:hover{background-color:#bc271c}.button--destructive:disabled:focus,.button--destructive:disabled:active,.button--destructive:disabled:hover,.button--destructive.button--disabled:focus,.button--destructive.button--disabled:active,.button--destructive.button--disabled:hover,.button--inline-destructive:disabled:focus,.button--inline-destructive:disabled:active,.button--inline-destructive:disabled:hover,.button--inline-destructive.button--disabled:focus,.button--inline-destructive.button--disabled:active,.button--inline-destructive.button--disabled:hover{background-color:#df382c}.button--inline-destructive{display:inline-block;width:auto}.button--base,.button--inline-base{color:#333;background-color:transparent}.button--base:focus,.button--base:active,.button--base:hover,.button--inline-base:focus,.button--inline-base:active,.button--inline-base:hover{background-color:rgba(0,0,0,0.1)}.button--base:disabled:focus,.button--base:disabled:active,.button--base:disabled:hover,.button--base.button--disabled:focus,.button--base.button--disabled:active,.button--base.button--disabled:hover,.button--inline-base:disabled:focus,.button--inline-base:disabled:active,.button--inline-base:disabled:hover,.button--inline-base.button--disabled:focus,.button--inline-base.button--disabled:active,.button--inline-base.button--disabled:hover{background-color:transparent}.button--inline-base{display:inline-block;width:auto}.cookie-policy{box-shadow:0 -1px 2px rgba(0,0,0,0.2);background-color:#fae4dc;position:fixed;bottom:0;width:100%;z-index:100}.cookie-policy p{font-size:.8125em;margin-bottom:0;margin-left:0;padding:8px 0;width:100%}.cookie-policy .link-cta{background-image:url("https://assets.ubuntu.com/v1/3f057022-close-orange.svg");background-repeat:no-repeat;color:#fff;float:right;font-size:1em;height:15px;margin:12px 0;margin-top:12px;padding:0;text-decoration:none;text-indent:-9999px;width:16px}.no-svg .cookie-policy .link-cta,.opera-mini .cookie-policy .link-cta{background-image:url("https://assets.ubuntu.com/v1/898777ac-close-orange.png")}.deep-link{text-decoration:none}.deep-link:after{content:'';display:inline-block;margin-left:3px;opacity:0;position:relative;top:1px;width:1em;height:1em;background-image:url(https://assets.ubuntu.com/v1/128877a5-anchor_16.svg);background-position:0 80%;background-repeat:no-repeat;background-size:16px;transition:opacity .1s}.deep-link:hover{text-decoration:none}.deep-link:hover:after{opacity:1}.banner{background:#000;box-shadow:none}@media only screen and (min-width: 620px){.banner .nav-primary{width:auto}.banner ul{border-top:0;border-right:0;box-shadow:none}.banner li{width:auto;border-right:0;border-bottom:0}.banner li:last-child a{border-right:0}.banner a,.banner a:link,.banner a:visited{border-left:0}}@media only screen and (max-width: 768px){footer.global{padding-top:0}}.instruction{padding:0;position:relative;width:100%;border-bottom:1px dotted #ddd}.instruction .instruction__bullet,.instruction .instruction__details{padding:60px 40px;margin-bottom:0}.instruction .instruction__bullet{margin-right:0}.instruction .instruction__step{display:inline-block;border-radius:50%;width:50px;height:50px;line-height:50px;color:#fff;text-align:center;vertical-align:top;font-size:2em;font-weight:400}.instruction .instruction__title{display:inline;position:relative;top:6px;line-height:1.6}.instruction .command-line{position:relative;border-radius:4px;background-color:#2c001e;border:1px solid #2c001e;overflow:hidden;transition:all .2s}.instruction .command-line .command-line__input{border:0;background:transparent;font-size:1em;font-family:Ubuntu Mono;font-weight:300;padding:.7em 1em;color:#fff;width:100%}.instruction .command-line .command-line__copy-button{transition:background-color .2s;position:absolute;right:0;top:0;width:50px;border:0;height:100%;display:block;text-indent:-9999px;background-image:url("https://assets.ubuntu.com/v1/994e60f9-get-link-url_16.svg");background-repeat:no-repeat;background-position:center;background-color:#fff}.row{border-bottom:0;background-color:rgba(255,255,255,0.6)}@media only screen and (max-width: 768px){.row{padding-bottom:20px}}@media only screen and (max-width: 768px){.row-hero{padding-top:20px;margin-top:0}}.strip-white{background:#fff}.strip-dark{background:#333;color:#fff}.strip-dark a{color:#fff}.strip-trans{background:transparent}.social-list{list-style:none;padding-left:0;margin-left:0}.social-list__item--twitter,.social-list__item--google-plus,.social-list__item--facebook{margin-right:20px;float:left;text-indent:-99999px;background-image:url("https://assets.ubuntu.com/v1/ed986fc0-icon-social.svg");background-repeat:no-repeat;height:44px;width:44px;overflow:hidden;display:inline-block}.social-list__item--twitter:hover,.social-list__item--google-plus:hover,.social-list__item--facebook:hover{background-position-y:-45px}.social-list__item--twitter{background-position:0 0}.social-list__item--google-plus{background-position:-45px 0}.social-list__item--facebook{background-position:-90px 0}@media only screen and (min-width: 768px){.tooltip{position:relative;display:inline-block}.tooltip__content{position:absolute;z-index:98;left:-1000px;right:-1000px;top:-30px;font-weight:300;margin:auto;display:block;text-align:center;white-space:nowrap}.tooltip:hover .tooltip__content:after{display:table;z-index:98;margin:auto;color:#fff;border-radius:3px;background:#000;box-shadow:none;font-size:12px;content:attr(data-tooltip);padding:4px 6px;white-space:nowrap;text-align:center}.tooltip:hover .tooltip__content:before{position:absolute;top:100%;left:50%;margin-left:-5px;content:'';border:solid transparent;border-width:5px;border-top-color:#000}}a,.link{cursor:pointer;color:#f7f7f7;text-decoration:underline}pre{padding:10px 15px;background-color:#333;border:0;border-radius:2px;color:#cdcdcd}html{overflow-y:scroll;overflow-x:hidden;min-height:100%;color:#333;font:1em / 1.5 "'Ubuntu', Arial, 'libra sans', sans-serif"}a{color:#333;text-decoration:none;border-bottom:1px solid #c2c2c2}a:hover{text-decoration:none;color:#dd4814}p,li,code,pre{font-size:16px;font-size:1rem;line-height:1.5;margin-bottom:.75em}pre,code{text-align:left;white-space:pre-line;word-spacing:normal;word-wrap:break-word;tab-size:4;hyphens:none;direction:ltr}hr{margin-bottom:20px}h1,h2,h3,h4,h5{margin:10px 0;font-weight:300;line-height:1.5;color:#333}h1{font-size:2em}h1 small{font-size:75%}h2{font-size:1.4375em}h2 small{font-size:75%}h3{font-size:1.25em}h3 small{font-size:75%}h4{font-size:1em}h4 small{font-size:75%}h5{font-size:0.875em}h5 small{font-size:75%}.gigantic{font-size:45px;font-size:2.8125rem}.mega{font-size:40px;font-size:2.5rem}input[type='text'],input[type='number'],input[type='search'],input[type='password'],input[type='email'],input[type='url'],input[type='tel']{padding:7px 10px;box-shadow:none;width:100%}input[type='text']::placeholder,input[type='number']::placeholder,input[type='search']::placeholder,input[type='password']::placeholder,input[type='email']::placeholder,input[type='url']::placeholder,input[type='tel']::placeholder{color:#888}input[type='text'][disabled],input[type='number'][disabled],input[type='search'][disabled],input[type='password'][disabled],input[type='email'][disabled],input[type='url'][disabled],input[type='tel'][disabled]{color:#888;background-color:transparent}input[type='number']{padding-right:15px}input[type='search']{appearance:textfield}input[type='search']::-webkit-search-decoration,input[type='search']::-webkit-search-cancel-button{appearance:none}input[type='checkbox']{margin:4px 0 0;margin-top:1px 9;line-height:normal}input[type='radio'],input[type='image']{display:inline-block;margin-right:10px}textarea{height:auto;min-height:175px;padding:7px 10px !important;box-shadow:none;width:100%;line-height:20px}textarea::placeholder{color:#888}textarea[disabled]{color:#888;background-color:transparent}select{display:block;clear:both;cursor:pointer;margin:0;background-image:url("../assets/images/forms/chevron-down.svg");background-repeat:no-repeat;background-position:top 14px right 10px;background-color:#fff;padding:7px 30px 7px 10px !important;box-shadow:none;width:100%;-moz-appearance:none;appearance:none;text-indent:.01px;text-overflow:''}select[multiple],select[size]{height:auto;background-image:none;padding-top:10px}select[disabled]{color:#888;background-image:none;background-color:transparent}select:-moz-focusring{color:transparent;text-shadow:0 0 0 #000}select::-ms-expand{display:none}fieldset{background:none;margin-left:0;padding:0}label{display:inline-block;max-width:100%;margin-bottom:5px}[type="checkbox"]:not(:checked),[type="checkbox"]:checked{position:absolute;left:-9999px}[type="checkbox"]+label{height:16px;position:relative;padding-left:25px;cursor:pointer}[type="checkbox"]+label:before{content:'';position:absolute;left:0;top:3px;width:11px;height:11px;border:1px solid #cdcdcd;background:#fff;border-radius:2px}[type="checkbox"]:checked+label:before{background-color:#dd4814;border-color:#dd4814}[type="checkbox"]:checked+label:after{content:'✔';position:absolute;line-height:13px;top:3px;left:3px;font-size:10px;color:#fff;transition:all .2s}@-moz-document url-prefix(){[type="checkbox"]:checked+label:after{font-size:13px;left:1px}}[type="checkbox"][disabled]{cursor:not-allowed}[type="checkbox"][disabled]+label{cursor:not-allowed}[type="checkbox"][disabled]+label:before{opacity:.5}[type="radio"]:not(:checked),[type="radio"]:checked{position:absolute;left:-9999px}[type="radio"]+label{position:relative;padding-left:25px;cursor:pointer}[type="radio"]+label:before{content:'';position:absolute;left:0;top:1px;width:13px;height:13px;border:1px solid #cdcdcd;background:#fff;border-radius:50%}[type="radio"]:checked+label:after{content:'';position:absolute;left:3px;top:4px;width:9px;height:9px;background:#dd4814;border-radius:50%}.tags{width:100%}.tags .tag-list{width:100%;margin:0}.tags .tag-list .tag-item{display:inline-block;float:left;line-height:36px;margin-bottom:0;margin-right:10px;word-wrap:break-word}.tags .tag-list .tag-item .remove-button{border-bottom:0}.tags .input{width:100% !important}.onoffswitch{position:relative;display:inline-block;vertical-align:middle;width:38px;margin:0 0 4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.onoffswitch[disabled] .onoffswitch-checkbox,.onoffswitch[disabled] .onoffswitch-label{opacity:.5;pointer-events:none}.onoffswitch-external-label{display:inline-block;margin-left:5px}.onoffswitch-checkbox{display:none !important}.onoffswitch-label{display:block !important;overflow:hidden;cursor:pointer;border-radius:2px;padding-left:0 !important;margin:0 !important}.onoffswitch-label:before,.onoffswitch-label:after{display:none}.onoffswitch-inner{display:block;width:200%;margin-left:-100%;transition:margin .3s ease-in 0s}.onoffswitch-inner:before,.onoffswitch-inner:after{display:block;float:left;width:50%;height:18px;padding:0;line-height:18px;font-size:14px;color:#fff;font-family:Trebuchet, Arial, sans-serif;font-weight:bold;box-sizing:border-box}.onoffswitch-inner:before{content:'';padding-left:10px;background-color:#38b44a;color:#fff}.onoffswitch-inner:after{content:'';padding-right:10px;background-color:#eee;color:#888;text-align:right}.onoffswitch-switch{display:block;width:19px;height:16px;margin:0;background:#fff;position:absolute;top:0;bottom:0;right:19px;border:1px solid #d2d2d2;border-radius:2px;transition:all .3s ease-in 0s;box-sizing:border-box}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-inner{margin-left:0}.onoffswitch-checkbox:checked+.onoffswitch-label .onoffswitch-switch{right:0}dl dt{clear:left;color:#333}dl dd{color:#333;margin-left:0}dl dt,dl dd{display:inline-block;float:left;line-height:36px;margin-bottom:10px !important;word-wrap:break-word}dl dt:last-child,dl dd:last-child{margin-bottom:0}.wrapper{min-height:100%;height:auto !important;height:100%;margin:0 auto;position:relative;background:rgba(255,255,255,0.4)}.wrapper:after{content:'';position:absolute;display:block;top:0;right:0;bottom:0;left:0;background:url("../assets/images/backgrounds/background-paper.png");height:100%;width:100%;z-index:-1}.wrapper--inner{max-width:1440px;width:100%;margin:0 auto;clear:both;display:block}.row{background-color:transparent;border-top:1px dotted #cdcdcd}.row:first-of-type{border-top:0}.button--secondary{line-height:18px}.button--primary:hover,.button--destructive:hover,.button--positive:hover{color:#fff}.button--base:hover,.button--neutral:hover{color:#333}.button--large{height:auto;padding-top:15px;padding-bottom:15px}.button-group{position:relative;width:100%;vertical-align:middle;clear:both;min-width:160px}.button-group.button-group--inline{display:inline-block;width:auto}.button-group .button-group__link{display:inline-block;float:left;text-align:left;padding-right:35px}.button-group .button-group__link::after{content:'';display:inline-block;position:absolute;top:15px;right:15px;width:4px;height:4px;vertical-align:middle;border-right:1px solid;border-bottom:1px solid;transform:rotate(45deg)}.button-group .button-group__dropdown{border-radius:3px;left:0;margin:0;list-style:none;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);z-index:20;max-height:1000px;transition:max-height .3s ease-in;position:absolute;top:36px;clear:both;min-width:160px}.button-group .button-group__item{float:left;clear:both;padding:5px 10px;margin:0;width:100%}.button-group .button-group__item .button-group__item-link{color:#333;cursor:pointer;width:100%;float:left;margin:0;border:0;text-transform:lowercase;white-space:nowrap}.button-group .button-group__item .button-group__item-link:first-letter{text-transform:uppercase}.button-group .button-group__item .button-group__item-link:hover{color:#dd4814;text-decoration:none}.form .form__group{margin-bottom:10px}.form .form__group .form__group-input{position:relative}.form .form__group .form__group-input .form__group-remove{position:absolute;top:10px;right:10px;cursor:pointer;height:16px;width:16px;background:url("../assets/images/icons/remove.svg") no-repeat;background-size:75% 75%;background-position:center;display:inline-block}.form .form__group .form__group-input .icon,.form .form__group .form__group-input .icon--status-failed,.form .form__group .form__group-input .icon--status-in-progress,.form .form__group .form__group-input .icon--status-queued,.form .form__group .form__group-input .icon--status-succeeded,.form .form__group .form__group-input .icon--status-waiting{position:absolute;top:10px;right:10px;display:inline-block}.form .form__group.form__group--subtle .form__group-label{color:#888}.form .form__group.form__group--subtle input,.form .form__group.form__group--subtle select,.form .form__group.form__group--subtle textarea{border-color:#e3e3e3;background-color:transparent}.form .form__group.form__group--subtle input:hover,.form .form__group.form__group--subtle select:hover,.form .form__group.form__group--subtle textarea:hover{border-color:#cdcdcd;background-color:#fff;outline:none}.form .form__group.form__group--subtle input:active,.form .form__group.form__group--subtle input:focus,.form .form__group.form__group--subtle select:active,.form .form__group.form__group--subtle select:focus,.form .form__group.form__group--subtle textarea:active,.form .form__group.form__group--subtle textarea:focus{border-color:#888;background-color:#fff;outline:none}.form .form__fieldset .form__group:last-of-type{margin-bottom:0}.form .form__siblings{float:left;width:100%;margin-bottom:10px}.form .form__siblings:hover .form__group--subtle input,.form .form__siblings:hover .form__group--subtle select,.form .form__siblings:hover .form__group--subtle textarea{border-color:#cdcdcd;background-color:#fff;outline:none}.form .form__siblings.is-active .form__group--subtle input,.form .form__siblings.is-active .form__group--subtle select,.form .form__siblings.is-active .form__group--subtle textarea{border-color:#cdcdcd;background-color:#fff;outline:none}.form.form--stack .form__group{width:100%;float:left}.form.form--stack .form__group [class*='-col']{margin-bottom:0}.form.form--stack .form__group .form__group-label{margin-top:0;margin-bottom:0;line-height:34px;vertical-align:top}.form.form--stack .form__group .form__group-input{min-height:34px}.form.form--stack .form__group .form__group-input .onoffswitch{margin-top:9px}.form.form--inline .form__fieldset{display:inline-block;width:auto;margin:0;vertical-align:middle}.form.form--inline .form__group{display:inline-block;margin:0 20px 0 0;vertical-align:top}.form.form--inline .form__group .form__group-label{margin-bottom:0;margin-right:10px;vertical-align:top;line-height:34px}.form.form--inline .form__group .form__group-label .icon--left{position:absolute;left:0;top:12px}.form.form--inline .form__group input,.form.form--inline .form__group select,.form.form--inline .form__group .form__group-input{display:inline-block;width:auto;min-height:34px}.form.form--inline .form__group [type='checkbox']+label,.form.form--inline .form__group [type='radio']+label{line-height:34px}.form.form--inline .form__group .onoffswitch{margin:9px 0 0}.form.form--inline .form__group .onoffswitch-external-label{margin:0;vertical-align:middle;line-height:34px}.form .form__help-text{font-size:14px;font-size:0.875rem;color:#888}.form__error{margin:0;color:#df382c}.form__error .form__error-item{margin:6px 0}.code-block{overflow:auto;margin:.5em 0;padding:1em;border:1px solid #cdcdcd;border-radius:.5em}.code-block .code-block__line{float:left;clear:both;margin:0}.code-block.code-block--terminal{color:#fff;border:0;background-color:#333}.code-block.code-block--numbering{counter-reset:line-numbering}.code-block.code-block--numbering .code-block__line::before{width:1.5em;padding-right:1em;content:counter(line-numbering);counter-increment:line-numbering;user-select:none;text-align:right;pointer-events:none;opacity:.5}.flash-messages{width:100%;float:left;margin:0}.flash-messages .flash-messages__item{box-sizing:border-box;border-radius:2px;list-style:none;padding:15px 20px;margin:0 0 20px;background:#fff;background-position:top 50% left 15px;background-repeat:no-repeat;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.flash-messages .flash-messages__item:last-of-type{margin-bottom:0}.flash-messages .flash-messages__item.flash-messages__item--error{background:#fff url("../assets/images/icons/error.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.flash-messages .flash-messages__item.flash-messages__item--info{background:#fff url("../assets/images/icons/info.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.flash-messages .flash-messages__item.flash-messages__item--warning{background:#fff url("../assets/images/icons/warning.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.flash-messages .flash-messages__item.flash-messages__item--success{background:#fff url("../assets/images/icons/success.svg") no-repeat;background-size:16px 16px;background-position:15px center;padding-left:45px}.icon,.icon--status-failed,.icon--status-in-progress,.icon--status-queued,.icon--status-succeeded,.icon--status-waiting{width:16px;height:16px;padding:0;border-bottom:0 !important;display:inline-block;vertical-align:middle;text-indent:999em}.icon.icon--add,.icon--add.icon--status-failed,.icon--add.icon--status-in-progress,.icon--add.icon--status-queued,.icon--add.icon--status-succeeded,.icon--add.icon--status-waiting{background:url("../assets/images/icons/add.svg") no-repeat;background-size:100% 100%}.icon.icon--account,.icon--account.icon--status-failed,.icon--account.icon--status-in-progress,.icon--account.icon--status-queued,.icon--account.icon--status-succeeded,.icon--account.icon--status-waiting{background:url("../assets/images/icons/account.svg") no-repeat;background-size:100% 100%}.icon.icon--cross,.icon--cross.icon--status-failed,.icon--cross.icon--status-in-progress,.icon--cross.icon--status-queued,.icon--cross.icon--status-succeeded,.icon--cross.icon--status-waiting{background:url("../assets/images/icons/cross.svg") no-repeat;background-size:100% 100%}.icon.icon--cross-orange,.icon--cross-orange.icon--status-failed,.icon--cross-orange.icon--status-in-progress,.icon--cross-orange.icon--status-queued,.icon--cross-orange.icon--status-succeeded,.icon--cross-orange.icon--status-waiting{background:url("../assets/images/icons/cross-orange.svg") no-repeat;background-size:100% 100%}.icon.icon--debug,.icon--debug.icon--status-failed,.icon--debug.icon--status-in-progress,.icon--debug.icon--status-queued,.icon--debug.icon--status-succeeded,.icon--debug.icon--status-waiting{background:url("../assets/images/icons/debug.svg") no-repeat;background-size:100% 100%}.icon.icon--delete,.icon--delete.icon--status-failed,.icon--delete.icon--status-in-progress,.icon--delete.icon--status-queued,.icon--delete.icon--status-succeeded,.icon--delete.icon--status-waiting{background:url("../assets/images/icons/delete.svg") no-repeat;background-size:100% 100%}.icon.icon--edit,.icon--edit.icon--status-failed,.icon--edit.icon--status-in-progress,.icon--edit.icon--status-queued,.icon--edit.icon--status-succeeded,.icon--edit.icon--status-waiting{background:url("../assets/images/icons/edit.svg") no-repeat;background-size:100% 100%}.icon.icon--error,.icon--error.icon--status-failed,.icon--error.icon--status-in-progress,.icon--error.icon--status-queued,.icon--error.icon--status-succeeded,.icon--error.icon--status-waiting{background:url("../assets/images/icons/error.svg") no-repeat;background-size:100% 100%}.icon.icon--help,.icon--help.icon--status-failed,.icon--help.icon--status-in-progress,.icon--help.icon--status-queued,.icon--help.icon--status-succeeded,.icon--help.icon--status-waiting{background:url("../assets/images/icons/help.svg") no-repeat;background-size:100% 100%}.icon.icon--info,.icon--info.icon--status-failed,.icon--info.icon--status-in-progress,.icon--info.icon--status-queued,.icon--info.icon--status-succeeded,.icon--info.icon--status-waiting{background:url("../assets/images/icons/info.svg") no-repeat;background-size:100% 100%}.icon.icon--loading,.icon--loading.icon--status-failed,.icon--loading.icon--status-in-progress,.icon--loading.icon--status-queued,.icon--loading.icon--status-succeeded,.icon--loading.icon--status-waiting{background:url("../assets/images/icons/loading.png") no-repeat;background-size:100% 100%}.icon.icon--mount,.icon--mount.icon--status-failed,.icon--mount.icon--status-in-progress,.icon--mount.icon--status-queued,.icon--mount.icon--status-succeeded,.icon--mount.icon--status-waiting{background:url("../assets/images/icons/mount.svg") no-repeat;background-size:100% 100%}.icon.icon--unmount,.icon--unmount.icon--status-failed,.icon--unmount.icon--status-in-progress,.icon--unmount.icon--status-queued,.icon--unmount.icon--status-succeeded,.icon--unmount.icon--status-waiting{background:url("../assets/images/icons/unmount.svg") no-repeat;background-size:100% 100%}.icon.icon--partition,.icon--partition.icon--status-failed,.icon--partition.icon--status-in-progress,.icon--partition.icon--status-queued,.icon--partition.icon--status-succeeded,.icon--partition.icon--status-waiting{background:url("../assets/images/icons/partition.svg") no-repeat;background-size:100% 100%}.icon.icon--power-error,.icon--power-error.icon--status-failed,.icon--power-error.icon--status-in-progress,.icon--power-error.icon--status-queued,.icon--power-error.icon--status-succeeded,.icon--power-error.icon--status-waiting{background:url("../assets/images/icons/power-error.svg") no-repeat;background-size:100% 100%}.icon.icon--power-off,.icon--power-off.icon--status-failed,.icon--power-off.icon--status-in-progress,.icon--power-off.icon--status-queued,.icon--power-off.icon--status-succeeded,.icon--power-off.icon--status-waiting{background:url("../assets/images/icons/power-off.svg") no-repeat;background-size:100% 100%}.icon.icon--power-on,.icon--power-on.icon--status-failed,.icon--power-on.icon--status-in-progress,.icon--power-on.icon--status-queued,.icon--power-on.icon--status-succeeded,.icon--power-on.icon--status-waiting{background:url("../assets/images/icons/power-on.svg") no-repeat;background-size:100% 100%}.icon.icon--remove,.icon--remove.icon--status-failed,.icon--remove.icon--status-in-progress,.icon--remove.icon--status-queued,.icon--remove.icon--status-succeeded,.icon--remove.icon--status-waiting{background:url("../assets/images/icons/remove.svg") no-repeat;background-size:100% 100%}.icon.icon--success,.icon--success.icon--status-failed,.icon--success.icon--status-in-progress,.icon--success.icon--status-queued,.icon--success.icon--status-succeeded,.icon--success.icon--status-waiting{background:url("../assets/images/icons/success.svg") no-repeat;background-size:100% 100%}.icon.icon--success-mono,.icon--success-mono.icon--status-failed,.icon--success-mono.icon--status-in-progress,.icon--success-mono.icon--status-queued,.icon--success-mono.icon--status-succeeded,.icon--success-mono.icon--status-waiting{background:url("../assets/images/icons/success-mono.svg") no-repeat;background-size:100% 100%}.icon.icon--success-grey,.icon--success-grey.icon--status-failed,.icon--success-grey.icon--status-in-progress,.icon--success-grey.icon--status-queued,.icon--success-grey.icon--status-succeeded,.icon--success-grey.icon--status-waiting{background:url('data:image/svg+xml;utf8, ');background-size:100% 100%}.icon.icon--settings,.icon--settings.icon--status-failed,.icon--settings.icon--status-in-progress,.icon--settings.icon--status-queued,.icon--settings.icon--status-succeeded,.icon--settings.icon--status-waiting{background:url("../assets/images/icons/settings.svg") no-repeat;background-size:100% 100%}.icon.icon--sync,.icon--sync.icon--status-failed,.icon--sync.icon--status-in-progress,.icon--sync.icon--status-queued,.icon--sync.icon--status-succeeded,.icon--sync.icon--status-waiting{background:url("../assets/images/icons/sync.svg") no-repeat;background-size:100% 100%}.icon.icon--search,.icon--search.icon--status-failed,.icon--search.icon--status-in-progress,.icon--search.icon--status-queued,.icon--search.icon--status-succeeded,.icon--search.icon--status-waiting{background:url("../assets/images/icons/magnifying_glass.svg") no-repeat;background-size:100% 100%}.icon.icon--system-shutdown,.icon--system-shutdown.icon--status-failed,.icon--system-shutdown.icon--status-in-progress,.icon--system-shutdown.icon--status-queued,.icon--system-shutdown.icon--status-succeeded,.icon--system-shutdown.icon--status-waiting{background:url("../assets/images/icons/system-shutdown.svg") no-repeat;background-size:100% 100%}.icon.icon--tooltip,.icon--tooltip.icon--status-failed,.icon--tooltip.icon--status-in-progress,.icon--tooltip.icon--status-queued,.icon--tooltip.icon--status-succeeded,.icon--tooltip.icon--status-waiting{background:url("../assets/images/icons/tooltip.svg") no-repeat;background-size:100% 100%}.icon.icon--tags,.icon--tags.icon--status-failed,.icon--tags.icon--status-in-progress,.icon--tags.icon--status-queued,.icon--tags.icon--status-succeeded,.icon--tags.icon--status-waiting{background:url("../assets/images/icons/tags.svg") no-repeat;background-size:100% 100%}.icon.icon--tick,.icon--tick.icon--status-failed,.icon--tick.icon--status-in-progress,.icon--tick.icon--status-queued,.icon--tick.icon--status-succeeded,.icon--tick.icon--status-waiting{background:url("../assets/images/icons/tick.svg") no-repeat;background-size:100% 100%}.icon.icon--logical-volume,.icon--logical-volume.icon--status-failed,.icon--logical-volume.icon--status-in-progress,.icon--logical-volume.icon--status-queued,.icon--logical-volume.icon--status-succeeded,.icon--logical-volume.icon--status-waiting{background:url("../assets/images/icons/logical-volume.svg") no-repeat;background-size:100% 100%}.icon.icon--warning,.icon--warning.icon--status-failed,.icon--warning.icon--status-in-progress,.icon--warning.icon--status-queued,.icon--warning.icon--status-succeeded,.icon--warning.icon--status-waiting{background:url("../assets/images/icons/warning.svg") no-repeat;background-size:100% 100%}.icon.icon--warning-mono,.icon--warning-mono.icon--status-failed,.icon--warning-mono.icon--status-in-progress,.icon--warning-mono.icon--status-queued,.icon--warning-mono.icon--status-succeeded,.icon--warning-mono.icon--status-waiting{background:url("../assets/images/icons/warning-mono.svg") no-repeat}.icon.icon--open,.icon--open.icon--status-failed,.icon--open.icon--status-in-progress,.icon--open.icon--status-queued,.icon--open.icon--status-succeeded,.icon--open.icon--status-waiting{background:url("../assets/images/forms/chevron-down.svg") no-repeat;background-size:10px 4px;background-position:center 4px}.icon.icon--close,.icon--close.icon--status-failed,.icon--close.icon--status-in-progress,.icon--close.icon--status-queued,.icon--close.icon--status-succeeded,.icon--close.icon--status-waiting{background:url("../assets/images/forms/chevron-up.svg") no-repeat;background-size:10px 4px;background-position:center 4px}.icon--status-failed{content:url('data:image/svg+xml;utf8, ')}.icon--status-in-progress{content:url('data:image/svg+xml;utf8, ')}.icon--status-queued{content:url('data:image/svg+xml;utf8, ')}.icon--status-succeeded{content:url('data:image/svg+xml;utf8, ')}.icon--status-waiting{content:url('data:image/svg+xml;utf8, ')}.icon--small{width:14px;height:14px}.icon--large{width:20px;height:20px}.icon--mega{width:23px;height:23px}.icon--gigantic{width:32px;height:32px}h1 .icon,h1 .icon--status-failed,h1 .icon--status-in-progress,h1 .icon--status-queued,h1 .icon--status-succeeded,h1 .icon--status-waiting{margin-top:-6px}h2 .icon,h2 .icon--status-failed,h2 .icon--status-in-progress,h2 .icon--status-queued,h2 .icon--status-succeeded,h2 .icon--status-waiting,h3 .icon,h3 .icon--status-failed,h3 .icon--status-in-progress,h3 .icon--status-queued,h3 .icon--status-succeeded,h3 .icon--status-waiting{margin-top:-5px}h4 .icon,h4 .icon--status-failed,h4 .icon--status-in-progress,h4 .icon--status-queued,h4 .icon--status-succeeded,h4 .icon--status-waiting,h5 .icon,h5 .icon--status-failed,h5 .icon--status-in-progress,h5 .icon--status-queued,h5 .icon--status-succeeded,h5 .icon--status-waiting,p .icon,p .icon--status-failed,p .icon--status-in-progress,p .icon--status-queued,p .icon--status-succeeded,p .icon--status-waiting,label .icon,label .icon--status-failed,label .icon--status-in-progress,label .icon--status-queued,label .icon--status-succeeded,label .icon--status-waiting,button .icon,button .icon--status-failed,button .icon--status-in-progress,button .icon--status-queued,button .icon--status-succeeded,button .icon--status-waiting,a .icon,a .icon--status-failed,a .icon--status-in-progress,a .icon--status-queued,a .icon--status-succeeded,a .icon--status-waiting,th .icon,th .icon--status-failed,th .icon--status-in-progress,th .icon--status-queued,th .icon--status-succeeded,th .icon--status-waiting,td .icon,td .icon--status-failed,td .icon--status-in-progress,td .icon--status-queued,td .icon--status-succeeded,td .icon--status-waiting,.table__header .icon,.table__header .icon--status-failed,.table__header .icon--status-in-progress,.table__header .icon--status-queued,.table__header .icon--status-succeeded,.table__header .icon--status-waiting,.table__data .icon,.table__data .icon--status-failed,.table__data .icon--status-in-progress,.table__data .icon--status-queued,.table__data .icon--status-succeeded,.table__data .icon--status-waiting{margin-top:-3px}.list__tree{list-style:none;border-left:1px solid #d2d2d2;position:relative}.list__tree.list__tree--sub-level{margin-top:10px;margin-left:20px;clear:both}.list__tree.list__tree--sub-level .list__item .list__item-feedback{left:180px}.list__tree .list__item{list-style:none}.list__tree .list__item:before{content:'';width:12px;height:1px;background:#d2d2d2;display:inline-block;position:relative;top:-4px;margin-right:5px}.list__tree .list__item:last-child::after{content:'';width:4px;height:1em;position:absolute;display:block;left:-2px;bottom:-6px;background:#fafafa}.list__tree .list__item .list__item-feedback{position:relative;left:200px;margin-top:-24px}table,.table{border-color:#d2d2d2;border-spacing:0;overflow-x:scroll;margin:0 0 20px;width:100%;text-align:left;border-collapse:separate}table tr,.table table tr,table .table tr,.table .table__row{width:100%;border-color:#b2b2b2;border-bottom-style:dotted;border-bottom-width:1px}table th,.table table th,table .table th,table td,.table table td,table .table td,.table .table__header,.table .table__data{font-size:16px;font-size:1rem;padding:10px;box-sizing:border-box;min-height:21px;background:none;border:0;text-align:left;border-collapse:separate;vertical-align:top;backface-visibility:hidden;position:relative}table thead tr,.table table thead tr,table thead .table tr,.table .table__head .table__row{color:#888;border-bottom:1px solid}table thead tr:hover,.table .table__head .table__row:hover{background-color:transparent}table thead th,.table table thead th,table thead .table th,.table .table__head .table__header{font-size:13px;font-size:0.8125rem;background:none;color:#888;font-size:13px}table thead th input[type="radio"]+label,.table .table__head .table__header input[type="radio"]+label,table thead th input[type="checkbox"]+label,.table .table__head .table__header input[type="checkbox"]+label{margin:0;top:-3px}table thead th a:link,table thead th a:visited,.table .table__head .table__header .table__header-link{color:#888;border-bottom:0}table thead th a:hover:link,table thead th a:hover:visited,.table .table__head .table__header .table__header-link:hover{color:#333;text-decoration:none}table thead th a.is-active:link,table thead th a.is-active:visited,.table .table__head .table__header .is-active.table__header-link{color:#333;text-decoration:none}table thead th a.is-sorted:link,table thead th a.is-sorted:visited,.table .table__head .table__header .is-sorted.table__header-link{border-bottom:1px solid #333}table thead .divide,.table .table__head .table__header-divide,.table .table__head .divide{width:1px;display:inline-block;background:#888;height:10px;margin:0 5px}table .numerical,.table .numerical{text-align:right}table input[type="radio"]+label,.table input[type="radio"]+label,table input[type="checkbox"]+label,.table input[type="checkbox"]+label{margin:0;top:-1px}table th{color:#888;border-bottom:1px solid}table td{border-color:#b2b2b2;border-bottom-style:dotted;border-bottom-width:1px}table td a:link,table td a:visited{color:#333;border-bottom:1px solid #c2c2c2}table td a:link:hover,table td a:visited:hover{text-decoration:none;color:#dd4814}table thead{width:100% !important;display:table-header-group !important}table thead.table__head--sticky{background:#fff;padding:0;width:100vw !important;left:0 !important;right:0 !important;height:auto}table thead.table__head--sticky tr{max-width:1440px;width:100%;margin:0 auto;display:block;float:none;border-bottom:0;overflow:hidden}table thead.table__head--sticky tr th{border-bottom:0;float:left}.table{display:table}.table .table__row{float:left;display:table-row}.table .table__row:hover{background-color:#fff}.table .table__row:hover .table__controls{z-index:1;opacity:1}.table .table__row:hover .table__controls--secondary{z-index:1;opacity:1}.table .table__row.is-active{background-color:#fff}.table .table__row.is-active .table__controls{z-index:1;opacity:1}.table .table__row.is-active .table__controls--secondary{z-index:1;opacity:1}.table .table__row.is-active .table__dropdown .table__row{display:none}.table .table__row.is-active .table__dropdown .table__row.is-active{display:block}.table .table__header,.table .table__data{display:table-cell;float:left}.table .table__data a:link,.table .table__data a:visited{color:#333;border-bottom:1px solid #c2c2c2}.table .table__data a:link:hover,.table .table__data a:visited:hover{text-decoration:none;color:#dd4814}.table .table__head{display:table-head;width:100%;box-sizing:border-box;width:auto !important}.table .table__head.table__head--sticky{background:#fff;padding:0;width:100vw !important;left:0 !important;right:0 !important;border-top:1px dotted #d2d2d2;box-shadow:0 1px 1px rgba(0,0,0,0.1);height:auto !important}.table .table__head.table__head--sticky .table__row{max-width:1440px;width:100%;margin:0 auto;display:block;float:none;border-bottom:0;overflow:hidden}.table .table__body{display:table-row-group}.table .table__footer{display:table-footer-group}.table .table__label{clear:both;display:block;margin-top:5px;line-height:16px;color:#bcbcbc}.table .table__label a{color:#bcbcbc}.table .table__label a:hover{color:#f7f7f7}.table .table__label.is-active a{color:#f7f7f7}.table .table__controls{width:100%;text-align:right;opacity:0;z-index:-1000}.table .table__controls--secondary{opacity:0;z-index:-1000;width:auto;text-align:left}.table .table__controls a,.table .table__controls a:link,.table .table__controls a:visited{color:#333;border-bottom:1px solid #c2c2c2}.table .table__controls a:hover,.table .table__controls a:link:hover,.table .table__controls a:visited:hover{text-decoration:none;color:#dd4814}.table .table__input{display:inline-block;background-color:#fff;border-color:#d2d2d2;background-position:right 10px top 16px;margin:-7px 0}.table .table__input[disabled]{background-color:transparent;border-color:transparent;pointer-events:none;background-position:-9999px -9999px;color:#333}.table .table__dropdown{width:100%}.table .table__dropdown .table__row{border-bottom:0;display:none;position:relative}.table .table__dropdown .table__row--indent{padding-left:41px !important;float:left;width:100%;box-sizing:border-box}.table .table__dropdown .table__row:before{display:block;margin:0 auto;width:calc(100% - 20px);border-top:1px dotted #d2d2d2;position:absolute;height:1px;content:'';top:0;left:10px}.table .table__dropdown .table__row.table__row--head{border-bottom:0}.table .table__dropdown .table__row.table__row--head .table__header{color:#bcbcbc;font-size:13px}.table .table__dropdown .table__row.u-border--none:before{display:none}.table .table__dropdown .table__row.u-border:before{display:block;margin:0 auto;width:calc(100% - 20px);border-top:1px dotted #d2d2d2;position:absolute;height:1px;content:'';top:0;left:10px}.table .table__dropdown .table__row.is-active .table__input{background-color:#fff;border-color:#d2d2d2;background-position:right 10px top 16px;pointer-events:all}.table .table__dropdown .table__row.is-active .table__input[disabled]{border-color:transparent;cursor:pointer}.table .table__dropdown--info .table__row{border-bottom:0}.table .table__dropdown--info .table__data{color:#bcbcbc}.form .form__group input,.form .form__group select{margin:0}.table--error{border-color:#df382c}.table--error .table__header,.table--error .table__data,.table--error th,.table--error td{border-color:#df382c;background-color:#fadfdd}.table--warning{border-color:#eca918}.table--warning .table__header,.table--warning .table__data,.table--warning th,.table--warning td{border-color:#eca918;background-color:#fcefd4}.table--success{border-color:#38b44a}.table--success .table__header,.table--success .table__data,.table--success th,.table--success td{border-color:#38b44a;background-color:#caeecf}.table--information{border-color:#19b6ee}.table--information .table__header,.table--information .table__data,.table--information th,.table--information td{border-color:#19b6ee;background-color:#d7f2fc}.table-col--1{width:1%}.table-col--2{width:2%}.table-col--3{width:3%}.table-col--4{width:4%}.table-col--5{width:5%}.table-col--6{width:6%}.table-col--7{width:7%}.table-col--8{width:8%}.table-col--9{width:9%}.table-col--10{width:10%}.table-col--11{width:11%}.table-col--12{width:12%}.table-col--13{width:13%}.table-col--14{width:14%}.table-col--15{width:15%}.table-col--16{width:16%}.table-col--17{width:17%}.table-col--18{width:18%}.table-col--19{width:19%}.table-col--20{width:20%}.table-col--21{width:21%}.table-col--22{width:22%}.table-col--23{width:23%}.table-col--24{width:24%}.table-col--25{width:25%}.table-col--26{width:26%}.table-col--27{width:27%}.table-col--28{width:28%}.table-col--29{width:29%}.table-col--30{width:30%}.table-col--31{width:31%}.table-col--32{width:32%}.table-col--33{width:33%}.table-col--34{width:34%}.table-col--35{width:35%}.table-col--36{width:36%}.table-col--37{width:37%}.table-col--38{width:38%}.table-col--39{width:39%}.table-col--40{width:40%}.table-col--41{width:41%}.table-col--42{width:42%}.table-col--43{width:43%}.table-col--44{width:44%}.table-col--45{width:45%}.table-col--46{width:46%}.table-col--47{width:47%}.table-col--48{width:48%}.table-col--49{width:49%}.table-col--50{width:50%}.table-col--51{width:51%}.table-col--52{width:52%}.table-col--53{width:53%}.table-col--54{width:54%}.table-col--55{width:55%}.table-col--56{width:56%}.table-col--57{width:57%}.table-col--58{width:58%}.table-col--59{width:59%}.table-col--60{width:60%}.table-col--61{width:61%}.table-col--62{width:62%}.table-col--63{width:63%}.table-col--64{width:64%}.table-col--65{width:65%}.table-col--66{width:66%}.table-col--67{width:67%}.table-col--68{width:68%}.table-col--69{width:69%}.table-col--70{width:70%}.table-col--71{width:71%}.table-col--72{width:72%}.table-col--73{width:73%}.table-col--74{width:74%}.table-col--75{width:75%}.table-col--76{width:76%}.table-col--77{width:77%}.table-col--78{width:78%}.table-col--79{width:79%}.table-col--80{width:80%}.table-col--81{width:81%}.table-col--82{width:82%}.table-col--83{width:83%}.table-col--84{width:84%}.table-col--85{width:85%}.table-col--86{width:86%}.table-col--87{width:87%}.table-col--88{width:88%}.table-col--89{width:89%}.table-col--90{width:90%}.table-col--91{width:91%}.table-col--92{width:92%}.table-col--93{width:93%}.table-col--94{width:94%}.table-col--95{width:95%}.table-col--96{width:96%}.table-col--97{width:97%}.table-col--98{width:98%}.table-col--99{width:99%}.table-col--100{width:100%}.tabs{display:inline-block}.tabs .tabs__tab{font-size:18px;font-size:1.125rem;display:inline-block;list-style-type:none}.tabs .tabs__tab .tabs__tab-link{color:#888;text-decoration:none;border-bottom:0}.tabs .tabs__tab:hover .tabs__tab-link{border-bottom:3px solid #888}.tabs .tabs__tab:after{content:'';width:1px;display:inline-block;background:#d2d2d2;height:11px;padding:0;margin:0 5px}.tabs .tabs__tab:last-child::after{display:none}.tabs .is-active .tabs__tab-link,.tabs .is-active:hover .tabs__tab-link{border-bottom:3px solid #dd4814}.tooltip{position:relative}.tooltip::before{position:absolute;left:50%;transform:translateX(-50%) translateY(-14px);content:'';width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;z-index:-1;border-top:5px solid #333;opacity:0;visibility:hidden;bottom:100%;margin-bottom:-11px}.tooltip::after{content:attr(aria-label);font-size:13px;font-weight:400;line-height:16px;position:absolute;z-index:-1;left:50%;bottom:100%;transform:translateX(-50%) translateY(-8px);background:#333;color:#fff;padding:10px;height:auto;text-indent:0;opacity:0;visibility:hidden;border-radius:5px;box-shadow:0 1px 3px 0 rgba(51,51,51,0.2);white-space:pre;box-sizing:border-box;font-style:normal}.tooltip:hover::after,.tooltip:hover::before{opacity:1 !important;z-index:1000;visibility:visible}.tooltip.tooltip--right::before{border-top:5px solid transparent;border-right:5px solid #333;border-bottom:5px solid transparent;left:100%;bottom:inherit;top:50%;transform:translateX(4px) translateY(-50%)}.tooltip.tooltip--right::after{left:100%;bottom:inherit;top:50%;margin-bottom:-14px;transform:translateX(14px) translateY(-50%)}.tooltip.tooltip--bottom::before{border-top:0;border-right:5px solid transparent;border-bottom:5px solid #333;border-left:5px solid transparent;left:50%;bottom:inherit;top:100%;transform:translateX(-50%) translateY(8px)}.tooltip.tooltip--bottom::after{left:50%;bottom:inherit;top:100%;margin-bottom:0;transform:translateX(-50%) translateY(13px)}.tooltip.tooltip--left::before{border-top:5px solid transparent;border-left:5px solid #333;border-bottom:5px solid transparent;left:-4px;bottom:inherit;top:50%;transform:translateX(-100%) translateY(-50%)}.tooltip.tooltip--left::after{left:-14px;bottom:inherit;top:50%;margin-bottom:-14px;transform:translateX(-100%) translateY(-50%)}.accordion{box-sizing:border-box;border-radius:2px;list-style:none;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);margin-bottom:40px}.accordion.is-disabled{opacity:.5;pointer-events:none}.accordion .accordion__title{font-size:20px;font-size:1.25rem;border-bottom:1px dotted #b2b2b2;padding:8px 20px 9px;margin:0}.accordion .accordion__tab{border-bottom:1px dotted #b2b2b2}.accordion .accordion__tab:last-of-type{border:0}.accordion .accordion__tab .accordion__tab-title.is-active{background-image:url("../assets/images/forms/chevron_up.svg")}.accordion .accordion__tab .accordion__tab-title.is-active+.accordion__tab-content{max-height:400px;transition:max-height .5s ease-in}.accordion .accordion__tab .accordion__tab-content{max-height:0;transition:max-height .5s ease-out;overflow:hidden;overflow:auto}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list{list-style-type:none;padding:0 20px 14px;margin:0}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item{margin-bottom:.15em}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item .accordion__tab-link{box-sizing:border-box;color:#333;width:100%;display:inline-block;padding-right:20px;border-bottom:0;outline:0}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item .accordion__tab-link:hover{color:#dd4814;text-decoration:none}.disabled .accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item .accordion__tab-link{color:#333}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active{margin-bottom:.15em;font-weight:400}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active .accordion__tab-link{background:transparent url("../assets/images/icons/cross.svg") top 7px right 0 no-repeat;text-decoration:none}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active:hover{color:#f7f7f7}.accordion .accordion__tab .accordion__tab-content .accordion__tab-list .accordion__tab-item.is-active:hover .accordion__tab-link{color:#dd4814;background-image:url("../assets/images/icons/cross_orange.svg")}.accordion .accordion__tab .accordion__tab-title,.accordion .accordion__tab .accordion__tab-title.is-active{padding:12px 20px;margin:0;color:#888;cursor:pointer;background:transparent url("../assets/images/forms/chevron_down.svg") top 20px right 20px no-repeat}.action-card{background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);margin-bottom:40px;padding:20px 20px 10px;box-sizing:border-box;width:100%;display:inline-block;clear:both}.action-card .action-card__title{font-size:23px;margin-bottom:20px}.action-card .action-card__controls{border-top:1px dotted #888;width:100%;display:inline-block;margin:10px 0 0;padding:10px 0 0;box-sizing:border-box}.banner{background-color:#000}@media only screen and (min-width: 620px){.banner{box-shadow:none}.banner .nav-primary{max-width:none}.banner .nav-primary .logo{height:auto;margin-top:12px}.banner .nav-primary .logo a{border-bottom:0}.banner .nav-primary ul li{border-left:1px solid #333}.banner .nav-primary ul li:last-child{border-right:1px solid #333;border-left:1px solid #333}.banner .nav-primary ul li a,.banner .nav-primary ul li a:link,.banner .nav-primary ul li a:visited{box-sizing:border-box;border-bottom:0;border-left:0}.banner .nav-primary ul li a:hover,.banner .nav-primary ul li a:link:hover,.banner .nav-primary ul li a:visited:hover{background-color:rgba(51,51,51,0.9)}.banner .nav-primary ul li a.active,.banner .nav-primary ul li a:link.active,.banner .nav-primary ul li a:visited.active{background-color:transparent;box-shadow:inset 0px -3px #dd4814}.banner .nav-primary ul li a.active:hover,.banner .nav-primary ul li a:link.active:hover,.banner .nav-primary ul li a:visited.active:hover{background-color:rgba(51,51,51,0.9)}}.page-header{box-sizing:border-box;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);padding:0 20px;height:auto !important;width:100%;float:left;margin-bottom:20px}.page-header .page-header__title{font-size:32px;font-size:2rem;margin:0;padding:34px 0;display:inline-block}.page-header .page-header__title [contenteditable="true"]{font-size:32px;font-size:2rem;padding:8px 10px;width:auto;box-sizing:border-box;border:1px solid transparent;margin:-20px 0 -20px -10px;border-radius:2px;color:#333;cursor:default;display:inline-block}.page-header .page-header__title [contenteditable="true"].editmode,.page-header .page-header__title [contenteditable="true"].editable:hover{border:1px solid #D2D2D2;cursor:text}.page-header .page-header__title [contenteditable="true"].editmode:hover,.page-header .page-header__title [contenteditable="true"]:active,.page-header .page-header__title [contenteditable="true"]:focus{outline:none;background-color:#fff;border:1px solid #B2B2B2}.page-header .page-header__title [contenteditable="true"].invalid,.page-header .page-header__title [contenteditable="true"].invalid:hover,.page-header .page-header__title [contenteditable="true"].invalid:active,.page-header .page-header__title [contenteditable="true"].invalid:focus{border-color:#df382c}.page-header .page-header__title .page-header__title-dot{display:inline-block;width:auto;padding:0}.page-header .page-header__title .page-header__title-domain{font-size:32px;font-size:2rem;display:inline-block;width:auto;min-height:66px;max-height:66px;line-height:25px;margin:-20px 0;background-position:top 32px right 10px;padding:13px 10px !important}.page-header .page-header__status{font-size:19px;font-size:1.1875rem;width:auto;display:inline-block;position:relative;margin-left:20px;color:#888}.page-header .page-header__status .page-header__status-check{font-size:16px;font-size:1rem;margin-left:10px;color:inherit;border:0}.page-header .page-header__status .page-header__status-check:hover{text-decoration:underline}.page-header .page-header__controls{padding:40px 0;display:inline-block;float:right}.page-header .page-header__controls .page-header__controls-feedback{color:#333;text-decoration:none;border-bottom:1px solid #c2c2c2}.page-header .page-header__controls .page-header__controls-feedback:hover{text-decoration:none;color:#dd4814;cursor:pointer}.page-header .page-header__dropdown{max-height:0;overflow:hidden;-webkit-transition:max-height 0.3s ease;-moz-transition:max-height 0.3s ease;transition:max-height 0.3s ease}.page-header .page-header__dropdown.is-open{max-height:1000px}.page-header .page-header__dropdown .page-header__section{border-top:1px dotted #888;padding-top:20px;padding-bottom:20px}.page-header .page-header__dropdown .page-header__message{width:auto;display:inline-block;float:none;margin:0 20px 0 0;line-height:36px}.page-header .page-header__dropdown .page-header__message--error{background:url("../assets/images/icons/error.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__message--info{background:url("../assets/images/icons/info.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__message--warning{background:url("../assets/images/icons/warning.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__message--success{background:url("../assets/images/icons/success.svg") no-repeat;background-size:16px 16px;background-position:0 10px;padding-left:25px}.page-header .page-header__dropdown .page-header__controls{padding:0;float:right}.search{position:relative}.search .search__input{font-size:16px;font-size:1rem;box-sizing:border-box;border-radius:4px;list-style:none;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);width:100%;border:0;padding:15px;appearance:textfield}.search .search__input::placeholder{color:#888}.search .search__input::-ms-clear{display:none}.search .search__input::-webkit-search-decoration,.search .search__input::-webkit-search-cancel-button,.search .search__input::-webkit-search-results-button,.search .search__input::-webkit-search-results-decoration{display:none}.search .search__input:disabled,.search .search__input.search--disabled{background-color:#fff;opacity:.5;pointer-events:none}.search .search__input:disabled+.search__submit,.search .search__input.search--disabled+.search__submit{pointer-events:none;opacity:.5}.search .search__submit{position:absolute;top:15px;right:15px;background-color:transparent;background-image:url("../assets/images/icons/magnifying_glass.svg");background-position:center;background-repeat:no-repeat;background-size:20px;text-indent:-999em;display:block;width:20px;height:20px;overflow:hidden;outline:none;padding:0;border:0}.search .search__submit:hover{background-color:transparent;background-image:url("../assets/images/icons/magnifying_glass.svg")}.search .search__submit.search__submit--close{background-image:url("../assets/images/icons/remove.svg");background-size:16px}.u-animation--spin{animation:spin 1s infinite linear !important}.u-animation--pulse{animation:pulse 1s ease-out !important;animation-iteration-count:infinite !important;opacity:0}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@keyframes pulse{0%{transform:scale(0.1, 0.1);opacity:0}50%{opacity:1}100%{transform:scale(1.2, 1.2);opacity:0}}.u-border{border:1px dotted #cdcdcd !important}.u-border--top{border-top:1px dotted #cdcdcd !important}.u-border--right{border-right:1px dotted #cdcdcd !important}.u-border--bottom{border-bottom:1px dotted #cdcdcd !important}.u-border--left{border-left:1px dotted #cdcdcd !important}.u-border--dotted{border-style:dotted !important}.u-border--solid{border-style:solid !important}.u-border--dashed{border-style:dashed !important}.u-border--none{border:0 !important}.u-align--right{text-align:right !important}.u-align--left{text-align:left !important}.u-align--center{text-align:center !important}.u-position--relative{position:relative !important}.u-position--fixed{position:fixed !important}.u-position--absolute{position:absolute !important}.u-float--right{float:right !important}.u-float--left{float:left !important}.u-float--none{float:none !important}.u-muted{opacity:.5 !important;filter:alpha(opacity=50) !important}.u-clear{clear:both !important}.u-display--block{display:block !important}.u-display--inline{display:inline !important}.u-display--inline-block{display:inline-block !important}.u-display--hidden{display:none !important}.u-width--full{width:100% !important}.u-width--half{width:50% !important}.u-width--third{width:calc(100%/3) !important}.u-width--quater{width:25% !important}.u-margin{margin:20px}.u-margin--none{margin:0 !important}.u-margin--tiny{margin:5px !important}.u-margin--small{margin:10px !important}.u-margin--medium{margin:30px !important}.u-margin--large{margin:40px !important}.u-margin--huge{margin:80px !important}.u-margin--top{margin-top:20px !important}.u-margin--top-none{margin-top:0 !important}.u-margin--top-tiny{margin-top:5px !important}.u-margin--top-small{margin-top:10px !important}.u-margin--top-medium{margin-top:30px !important}.u-margin--top-large{margin-top:40px !important}.u-margin--top-huge{margin-top:80px !important}.u-margin--right{margin-right:20px !important}.u-margin--right-none{margin-right:0 !important}.u-margin--right-tiny{margin-right:5px !important}.u-margin--right-small{margin-right:10px !important}.u-margin--right-medium{margin-right:30px !important}.u-margin--right-large{margin-right:40px !important}.u-margin--right-huge{margin-right:80px !important}.u-margin--bottom{margin-bottom:20px !important}.u-margin--bottom-none{margin-bottom:0 !important}.u-margin--bottom-tiny{margin-bottom:5px !important}.u-margin--bottom-small{margin-bottom:10px !important}.u-margin--bottom-medium{margin-bottom:30px !important}.u-margin--bottom-large{margin-bottom:40px !important}.u-margin--bottom-huge{margin-bottom:80px !important}.u-margin--left{margin-left:20px !important}.u-margin--left-none{margin-left:0 !important}.u-margin--left-tiny{margin-left:5px !important}.u-margin--left-small{margin-left:10px !important}.u-margin--left-medium{margin-left:30px !important}.u-margin--left-large{margin-left:40px !important}.u-margin--left-huge{margin-left:80px !important}.u-padding{padding:20px}.u-padding--none{padding:0 !important}.u-padding--tiny{padding:5px !important}.u-padding--small{padding:10px !important}.u-padding--medium{padding:30px !important}.u-padding--large{padding:40px !important}.u-padding--huge{padding:80px !important}.u-padding--top{padding-top:20px !important}.u-padding--top-none{padding-top:0 !important}.u-padding--top-tiny{padding-top:5px !important}.u-padding--top-small{padding-top:10px !important}.u-padding--top-medium{padding-top:30px !important}.u-padding--top-large{padding-top:40px !important}.u-padding--top-huge{padding-top:80px !important}.u-padding--right{padding-right:20px !important}.u-padding--right-none{padding-right:0 !important}.u-padding--right-tiny{padding-right:5px !important}.u-padding--right-small{padding-right:10px !important}.u-padding--right-medium{padding-right:30px !important}.u-padding--right-large{padding-right:40px !important}.u-padding--right-huge{padding-right:80px !important}.u-padding--bottom{padding-bottom:20px !important}.u-padding--bottom-none{padding-bottom:0 !important}.u-padding--bottom-tiny{padding-bottom:5px !important}.u-padding--bottom-small{padding-bottom:10px !important}.u-padding--bottom-medium{padding-bottom:30px !important}.u-padding--bottom-large{padding-bottom:40px !important}.u-padding--bottom-huge{padding-bottom:80px !important}.u-padding--left{padding-left:20px !important}.u-padding--left-none{padding-left:0 !important}.u-padding--left-tiny{padding-left:5px !important}.u-padding--left-small{padding-left:10px !important}.u-padding--left-medium{padding-left:30px !important}.u-padding--left-large{padding-left:40px !important}.u-padding--left-huge{padding-left:80px !important}.u-text--subtle{color:#888;font-size:.9375rem}.u-text--error{color:#df382c !important}.u-border--error{border-color:#df382c !important}.u-background--error{background-color:#df382c !important}.has-error{border:1px solid #df382c !important}.has-error:focus{border:1px solid #df382c !important}.u-text--off{color:#d2d2d2 !important}.u-border--off{border-color:#d2d2d2 !important}.u-background--off{background-color:#d2d2d2 !important}.has-off{border:1px solid #d2d2d2 !important}.has-off:focus{border:1px solid #d2d2d2 !important}.u-text--warning{color:#eca918 !important}.u-border--warning{border-color:#eca918 !important}.u-background--warning{background-color:#eca918 !important}.has-warning{border:1px solid #eca918 !important}.has-warning:focus{border:1px solid #eca918 !important}.u-text--success{color:#38b44a !important}.u-border--success{border-color:#38b44a !important}.u-background--success{background-color:#38b44a !important}.has-success{border:1px solid #38b44a !important}.has-success:focus{border:1px solid #38b44a !important}.u-text--on{color:#38b44a !important}.u-border--on{border-color:#38b44a !important}.u-background--on{background-color:#38b44a !important}.has-on{border:1px solid #38b44a !important}.has-on:focus{border:1px solid #38b44a !important}.u-text--information{color:#19b6ee !important}.u-border--information{border-color:#19b6ee !important}.u-background--information{background-color:#19b6ee !important}.has-information{border:1px solid #19b6ee !important}.has-information:focus{border:1px solid #19b6ee !important}.u-text--loading{color:#2ab7ec !important}.u-border--loading{border-color:#2ab7ec !important}.u-background--loading{background-color:#2ab7ec !important}.has-loading{border:1px solid #2ab7ec !important}.has-loading:focus{border:1px solid #2ab7ec !important}.u-vertical-align{position:relative;top:50%;transform:translateY(-50%)}.u-text--truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.hidden{display:none}.small-icon{width:12px}.images-info{text-align:center;padding:10px}.images-warning{box-sizing:border-box;border-radius:2px;list-style:none;padding:15px 20px 15px 45px;background-repeat:no-repeat;box-shadow:0 1px 1px rgba(0,0,0,0.1);background:#fff url("../assets/images/icons/error.svg") no-repeat;background-size:16px 16px;background-position:15px center;margin-bottom:20px}#loader{width:10px;margin:16px auto 0 auto}#importing{box-sizing:border-box;border-radius:2px;list-style:none;padding:15px;font-size:16px;margin:0 0 20px;background:#fff;background-position:top 50% left 15px;background-repeat:no-repeat;box-shadow:0 1px 1px rgba(0,0,0,0.1)}#importing .spinner{margin-top:-3px}.spinner{width:16px;height:16px;padding:0;border-bottom:0 !important;display:inline-block;vertical-align:middle;text-indent:999em;background:url("../assets/images/icons/loading.png") no-repeat;background-size:100% 100%}.spin{animation:spin 1s infinite linear !important}.importing-dot{opacity:0;-webkit-animation:dot 1.3s infinite;animation:dot 1.3s infinite}.accounts .logout .divide{padding:0 20px 0 30px;display:inline-block}.accounts .api li{position:relative}.accounts .api li input[type='text']{line-height:30px;padding-right:30px;width:100%}.accounts .api li input[type='text']::-webkit-input-placeholder{color:#333}.accounts .api li input[type='text']:-moz-placeholder{color:#333}.accounts .api li input[type='text']::-moz-placeholder{color:#333}.accounts .api li input[type='text']:-ms-input-placeholder{color:#333}.accounts .api li .delete-link{position:absolute;top:13px;right:15px}.script-content{width:100%;overflow:hidden}.script-content .content{margin:20px 0 0} diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/3rdparty/sticky.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/3rdparty/sticky.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/3rdparty/sticky.js 1969-12-31 19:00:00.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/3rdparty/sticky.js 2016-12-21 07:56:19.000000000 -0500 @@ -0,0 +1,684 @@ +/** + * ngSticky - https://github.com/d-oliveros/ngSticky + * + * A simple, pure javascript (No jQuery required!) AngularJS directive + * to make elements stick when scrolling down. + * + * Credits: https://github.com/d-oliveros/ngSticky/graphs/contributors + */ +(function() { + 'use strict'; + + var module = angular.module('sticky', []); + + /** + * Directive: sticky + */ + module.directive('sticky', ['$window', '$timeout', function($window, $timeout) { + return { + restrict: 'A', // this directive can only be used as an attribute. + scope: { + disabled: '=disabledSticky' + }, + link: function linkFn($scope, $elem, $attrs) { + + // Initial scope + var scrollableNodeTagName = 'sticky-scroll'; + var initialPosition = $elem.css('position'); + var initialStyle = $elem.attr('style') || ''; + var stickyBottomLine = 0; + var isSticking = false; + var onStickyHeighUnbind; + var originalInitialCSS; + var originalOffset; + var placeholder; + var stickyLine; + var initialCSS; + + // Optional Classes + var stickyClass = $attrs.stickyClass || ''; + var unstickyClass = $attrs.unstickyClass || ''; + var bodyClass = $attrs.bodyClass || ''; + var bottomClass = $attrs.bottomClass || ''; + + // Find scrollbar + var scrollbar = deriveScrollingViewport ($elem); + + // Define elements + var windowElement = angular.element($window); + var scrollbarElement = angular.element(scrollbar); + var $body = angular.element(document.body); + + // Resize callback + var $onResize = function () { + if ($scope.$root && !$scope.$root.$$phase) { + $scope.$apply(onResize); + } else { + onResize(); + } + }; + + // Define options + var usePlaceholder = ($attrs.usePlaceholder !== 'false'); + var anchor = $attrs.anchor === 'bottom' ? 'bottom' : 'top'; + var confine = ($attrs.confine === 'true'); + + // flag: can react to recalculating the initial CSS dimensions later + // as link executes prematurely. defaults to immediate checking + var isStickyLayoutDeferred = $attrs.isStickyLayoutDeferred !== undefined + ? ($attrs.isStickyLayoutDeferred === 'true') + : false; + + // flag: is sticky content constantly observed for changes. + // Should be true if content uses ngBind to show text + // that may vary in size over time + var isStickyLayoutWatched = $attrs.isStickyLayoutWatched !== undefined + ? ($attrs.isStickyLayoutWatched === 'true') + : true; + + + var offset = $attrs.offset + ? parseInt ($attrs.offset.replace(/px;?/, '')) + : 0; + + /** + * Trigger to initialize the sticky + * Because of the `timeout()` method for the call of + * @type {Boolean} + */ + var shouldInitialize = true; + + /** + * Initialize Sticky + */ + function initSticky() { + + if (shouldInitialize) { + + // Listeners + scrollbarElement.on('scroll', checkIfShouldStick); + windowElement.on('resize', $onResize); + + memorizeDimensions(); // remember sticky's layout dimensions + + // Setup watcher on digest and change + $scope.$watch(onDigest, onChange); + + // Clean up + $scope.$on('$destroy', onDestroy); + shouldInitialize = false; + } + }; + + /** + * need to recall sticky's DOM attributes (make sure layout has occured) + */ + function memorizeDimensions() { + // immediate assignment, but there is the potential for wrong values if content not ready + initialCSS = $scope.getInitialDimensions(); + + // option to calculate the dimensions when layout is 'ready' + if (isStickyLayoutDeferred) { + + // logic: when this directive link() runs before the content has had a chance to layout on browser, height could be 0 + if (!$elem[0].getBoundingClientRect().height) { + + onStickyHeighUnbind = $scope.$watch( + function() { + return $elem.height(); + }, + + // state change: sticky content's height set + function onStickyContentLayoutInitialHeightSet(newValue, oldValue) { + if (newValue > 0) { + // now can memorize + initialCSS = $scope.getInitialDimensions(); + + if (!isStickyLayoutWatched) { + // preference was to do just a one-time async watch on the sticky's content; now stop watching + onStickyHeighUnbind(); + } + } + } + ); + } + } + } + + /** + * Determine if the element should be sticking or not. + */ + var checkIfShouldStick = function() { + if ($scope.disabled === true || mediaQueryMatches()) { + if (isSticking) unStickElement(); + return false; + } + + // What's the document client top for? + var scrollbarPosition = scrollbarYPos(); + var shouldStick; + + if (anchor === 'top') { + if (confine === true) { + shouldStick = scrollbarPosition > stickyLine && scrollbarPosition <= stickyBottomLine; + } else { + shouldStick = scrollbarPosition > stickyLine; + } + } else { + shouldStick = scrollbarPosition <= stickyLine; + } + + // Switch the sticky mode if the element crosses the sticky line + // $attrs.stickLimit - when it's equal to true it enables the user + // to turn off the sticky function when the elem height is + // bigger then the viewport + var closestLine = getClosest (scrollbarPosition, stickyLine, stickyBottomLine); + + if (shouldStick && !shouldStickWithLimit ($attrs.stickLimit) && !isSticking) { + stickElement (closestLine); + } else if (!shouldStick && isSticking) { + unStickElement(closestLine, scrollbarPosition); + } else if (confine && !shouldStick) { + // If we are confined to the parent, refresh, and past the stickyBottomLine + // We should 'remember' the original offset and unstick the element which places it at the stickyBottomLine + originalOffset = elementsOffsetFromTop ($elem[0]); + unStickElement (closestLine, scrollbarPosition); + } + }; + + /** + * determine the respective node that handles scrolling, defaulting to browser window + */ + function deriveScrollingViewport(stickyNode) { + // derive relevant scrolling by ascending the DOM tree + var match =findAncestorTag (scrollableNodeTagName, stickyNode); + return (match.length === 1) ? match[0] : $window; + } + + /** + * since jqLite lacks closest(), this is a pseudo emulator (by tag name) + */ + function findAncestorTag(tag, context) { + var m = []; // nodelist container + var n = context.parent(); // starting point + var p; + + do { + var node = n[0]; // break out of jqLite + // limit DOM territory + if (node.nodeType !== 1) { + break; + } + + // success + if (node.tagName.toUpperCase() === tag.toUpperCase()) { + return n; + } + + p = n.parent(); + n = p; // set to parent + } while (p.length !== 0); + + return m; // empty set + } + + /** + * Seems to be undocumented functionality + */ + function shouldStickWithLimit(shouldApplyWithLimit) { + return shouldApplyWithLimit === 'true' + ? ($window.innerHeight - ($elem[0].offsetHeight + parseInt(offset)) < 0) + : false; + } + + /** + * Finds the closest value from a set of numbers in an array. + */ + function getClosest(scrollTop, stickyLine, stickyBottomLine) { + var closest = 'top'; + var topDistance = Math.abs(scrollTop - stickyLine); + var bottomDistance = Math.abs(scrollTop - stickyBottomLine); + + if (topDistance > bottomDistance) { + closest = 'bottom'; + } + + return closest; + } + + /** + * Unsticks the element + */ + function unStickElement(fromDirection) { + if (initialStyle) { + $elem.attr('style', initialStyle); + } + isSticking = false; + + initialCSS.width = $scope.getInitialDimensions().width; + + $body.removeClass(bodyClass); + $elem.removeClass(stickyClass); + $elem.addClass(unstickyClass); + + if (fromDirection === 'top') { + $elem.removeClass(bottomClass); + + $elem + .css('z-index', 10) + .css('width', initialCSS.width) + .css('top', initialCSS.top) + .css('position', initialCSS.position) + .css('left', initialCSS.cssLeft) + .css('margin-top', initialCSS.marginTop) + .css('height', initialCSS.height); + } else if (fromDirection === 'bottom' && confine === true) { + $elem.addClass(bottomClass); + + // It's possible to page down page and skip the 'stickElement'. + // In that case we should create a placeholder so the offsets don't get off. + createPlaceholder(); + + $elem + .css('z-index', 10) + .css('width', initialCSS.width) + .css('top', '') + .css('bottom', 0) + .css('position', 'absolute') + .css('left', initialCSS.cssLeft) + .css('margin-top', initialCSS.marginTop) + .css('margin-bottom', initialCSS.marginBottom) + .css('height', initialCSS.height); + } + + if (placeholder && fromDirection === anchor) { + placeholder.remove(); + } + } + + /** + * Sticks the element + */ + function stickElement(closestLine) { + // Set sticky state + isSticking = true; + $timeout(function() { + initialCSS.offsetWidth = $elem[0].offsetWidth; + }, 0); + $body.addClass(bodyClass); + $elem.removeClass(unstickyClass); + $elem.removeClass(bottomClass); + $elem.addClass(stickyClass); + + createPlaceholder(); + + $elem + .css('z-index', '10') + .css('width', $elem[0].offsetWidth + 'px') + .css('position', 'fixed') + .css('left', $elem.css('left').replace('px', '') + 'px') + .css(anchor, (offset + elementsOffsetFromTop (scrollbar)) + 'px') + .css('margin-top', 0); + + if (anchor === 'bottom') { + $elem.css('margin-bottom', 0); + } + } + + /** + * Clean up directive + */ + var onDestroy = function() { + scrollbarElement.off('scroll', checkIfShouldStick); + windowElement.off('resize', $onResize); + + $onResize = null; + + $body.removeClass(bodyClass); + + if (placeholder) { + placeholder.remove(); + } + }; + + /** + * Updates on resize. + */ + function onResize() { + unStickElement (anchor); + checkIfShouldStick(); + } + + /** + * Triggered on load / digest cycle + * return `0` if the DOM element is hidden + */ + var onDigest = function() { + if ($scope.disabled === true) { + return unStickElement(); + } + var offsetFromTop = elementsOffsetFromTop ($elem[0]); + if (offsetFromTop === 0) { + return offsetFromTop; + } + if (anchor === 'top') { + return (originalOffset || offsetFromTop) - elementsOffsetFromTop (scrollbar) + scrollbarYPos(); + } else { + return offsetFromTop - scrollbarHeight() + $elem[0].offsetHeight + scrollbarYPos(); + } + }; + + /** + * Triggered on change + */ + var onChange = function (newVal, oldVal) { + + /** + * Indicate if the DOM element is showed, or not + * @type {boolean} + */ + var elemIsShowed = !!newVal; + + /** + * Indicate if the DOM element was showed, or not + * @type {boolean} + */ + var elemWasHidden = !oldVal; + var valChange = (newVal !== oldVal || typeof stickyLine === 'undefined'); + var notSticking = (!isSticking && !isBottomedOut()); + + if (valChange && notSticking && newVal > 0 && elemIsShowed) { + stickyLine = newVal - offset; + //Update dimensions of sticky element when is showed + if (elemIsShowed && elemWasHidden) { + $scope.updateStickyContentUpdateDimensions($elem[0].offsetWidth, $elem[0].offsetHeight); + } + // IF the sticky is confined, we want to make sure the parent is relatively positioned, + // otherwise it won't bottom out properly + if (confine) { + $elem.parent().css({ + 'position': 'relative' + }); + } + + // Get Parent height, so we know when to bottom out for confined stickies + var parent = $elem.parent()[0]; + + // Offset parent height by the elements height, if we're not using a placeholder + var parentHeight = parseInt (parent.offsetHeight) - (usePlaceholder ? 0 : $elem[0].offsetHeight); + + // and now lets ensure we adhere to the bottom margins + // TODO: make this an attribute? Maybe like ignore-margin? + var marginBottom = parseInt ($elem.css('margin-bottom').replace(/px;?/, '')) || 0; + + // specify the bottom out line for the sticky to unstick + var elementsDistanceFromTop = elementsOffsetFromTop ($elem[0]); + var parentsDistanceFromTop = elementsOffsetFromTop (parent) + var scrollbarDistanceFromTop = elementsOffsetFromTop (scrollbar); + + var elementsDistanceFromScrollbarStart = elementsDistanceFromTop - scrollbarDistanceFromTop; + var elementsDistanceFromBottom = parentsDistanceFromTop + parentHeight - elementsDistanceFromTop; + + stickyBottomLine = elementsDistanceFromScrollbarStart + + elementsDistanceFromBottom + - $elem[0].offsetHeight + - marginBottom + - offset + + +scrollbarYPos(); + + checkIfShouldStick(); + } + }; + + /** + * Helper Functions + */ + + /** + * Create a placeholder + */ + function createPlaceholder() { + if (usePlaceholder) { + // Remove the previous placeholder + if (placeholder) { + placeholder.remove(); + } + + placeholder = angular.element('
'); + var elementsHeight = $elem[0].offsetHeight; + var computedStyle = $elem[0].currentStyle || window.getComputedStyle($elem[0]); + elementsHeight += parseInt(computedStyle.marginTop, 10); + elementsHeight += parseInt(computedStyle.marginBottom, 10); + elementsHeight += parseInt(computedStyle.borderTopWidth, 10); + elementsHeight += parseInt(computedStyle.borderBottomWidth, 10); + placeholder.css('height', $elem[0].offsetHeight + 'px'); + + $elem.after(placeholder); + } + } + + /** + * Are we bottomed out of the parent element? + */ + function isBottomedOut() { + if (confine && scrollbarYPos() > stickyBottomLine) { + return true; + } + + return false; + } + + /** + * Fetch top offset of element + */ + function elementsOffsetFromTop(element) { + var offset = 0; + + if (element.getBoundingClientRect) { + offset = element.getBoundingClientRect().top; + } + + return offset; + } + + /** + * Retrieves top scroll distance + */ + function scrollbarYPos() { + var position; + + if (typeof scrollbar.scrollTop !== 'undefined') { + position = scrollbar.scrollTop; + } else if (typeof scrollbar.pageYOffset !== 'undefined') { + position = scrollbar.pageYOffset; + } else { + position = document.documentElement.scrollTop; + } + + return position; + } + + /** + * Determine scrollbar's height + */ + function scrollbarHeight() { + var height; + + if (scrollbarElement[0] instanceof HTMLElement) { + // isn't bounding client rect cleaner than insane regex mess? + height = $window.getComputedStyle(scrollbarElement[0], null) + .getPropertyValue('height') + .replace(/px;?/, ''); + } else { + height = $window.innerHeight; + } + + return parseInt (height) || 0; + } + + /** + * Checks if the media matches + */ + function mediaQueryMatches() { + var mediaQuery = $attrs.mediaQuery || false; + var matchMedia = $window.matchMedia; + + return mediaQuery && !(matchMedia ('(' + mediaQuery + ')').matches || matchMedia (mediaQuery).matches); + } + + /** + * Get more accurate CSS values + */ + function getCSS($el, prop){ + var el = $el[0], + computed = window.getComputedStyle(el), + prevDisplay = computed.display, + val; + + // hide the element so that we can get original css + // values instead of computed values + el.style.display = "none"; + + // NOTE - computed style declaration object is a reference + // to the element's CSSStyleDeclaration, so it will always + // reflect the current style of the element + val = computed[prop]; + + // restore previous display value + el.style.display = prevDisplay; + + return val; + } + + // public accessors for the controller to hitch into. Helps with external API access + $scope.getElement = function() { return $elem; }; + $scope.getScrollbar = function() { return scrollbar; }; + $scope.getInitialCSS = function() { return initialCSS; }; + $scope.getAnchor = function() { return anchor; }; + $scope.isSticking = function() { return isSticking; }; + $scope.getOriginalInitialCSS = function() { return originalInitialCSS; }; + // pass through aliases + $scope.processUnStickElement = function(anchor) { unStickElement(anchor)}; + $scope.processCheckIfShouldStick =function() { checkIfShouldStick(); }; + + /** + * set the dimensions for the defaults of the content block occupied by the sticky element + */ + $scope.getInitialDimensions = function() { + return { + zIndex: $elem.css('z-index'), + top: $elem.css('top'), + position: initialPosition, // revert to true initial state + marginTop: $elem.css('margin-top'), + marginBottom: $elem.css('margin-bottom'), + cssLeft: getCSS($elem, 'left'), + width: $elem[0].offsetWidth, + height: $elem.css('height') + }; + }; + + /** + * only change content box dimensions + */ + $scope.updateStickyContentUpdateDimensions = function(width, height) { + if (width && height) { + initSticky(); + initialCSS.width = width + 'px'; + initialCSS.height = height + 'px'; + } + }; + + // ----------- configuration ----------- + + $timeout(function() { + originalInitialCSS = $scope.getInitialDimensions(); // preserve a copy + // Init the directive + initSticky(); + },0); + }, + + /** + * +++++++++ public APIs+++++++++++++ + */ + controller: ['$scope', '$window', function($scope, $window) { + + /** + * integration method allows for an outside client to reset the pinned state back to unpinned. + * Useful for when refreshing the scrollable DIV content completely + * if newWidth and newHeight integer values are not supplied then function will make a best guess + */ + this.resetLayout = function(newWidth, newHeight) { + + var scrollbar = $scope.getScrollbar(), + initialCSS = $scope.getInitialCSS(), + anchor = $scope.getAnchor(); + + function _resetScrollPosition() { + + // reset means content is scrolled to anchor position + if (anchor === 'top') { + // window based scroller + if (scrollbar === $window) { + $window.scrollTo(0, 0); + // DIV based sticky scroller + } else { + if (scrollbar.scrollTop > 0) { + scrollbar.scrollTop = 0; + } + } + } + // todo: need bottom use case + } + + // only if pinned, force unpinning, otherwise height is inadvertently reset to 0 + if ($scope.isSticking()) { + $scope.processUnStickElement (anchor); + $scope.processCheckIfShouldStick(); + } + // remove layout-affecting attribures that were modified by this sticky + $scope.getElement().css({ 'width': '', 'height': '', 'position': '', 'top': '', zIndex: '' }); + // model resets + initialCSS.position = $scope.getOriginalInitialCSS().position; // revert to original state + delete initialCSS.offsetWidth; // stickElement affected + + // use this directive element's as default, if no measurements passed in + if (newWidth === undefined && newHeight === undefined) { + var e_bcr = $scope.getElement()[0].getBoundingClientRect(); + newWidth = e_bcr.width; + newHeight = e_bcr.height; + } + + // update model with new dimensions (if supplied from client's own measurement) + $scope.updateStickyContentUpdateDimensions(newWidth, newHeight); // update layout dimensions only + + _resetScrollPosition(); + }; + + /** + * return a reference to the scrolling element (window or DIV with overflow) + */ + this.getScrollbar = function() { + return $scope.getScrollbar(); + }; + }] + }; + }] + ); + + // Shiv: matchMedia + window.matchMedia = window.matchMedia || (function() { + var warning = 'angular-sticky: This browser does not support ' + + 'matchMedia, therefore the minWidth option will not work on ' + + 'this browser. Polyfill matchMedia to fix this issue.'; + + if (window.console && console.warn) { + console.warn(warning); + } + + return function() { + return { + matches: true + }; + }; + }()); +}()); diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/controllers/dashboard.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/controllers/dashboard.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/controllers/dashboard.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/controllers/dashboard.js 2016-12-21 07:56:19.000000000 -0500 @@ -26,7 +26,7 @@ $rootScope.page = "dashboard"; // Set initial values. - $scope.loading = true; + $scope.loaded = false; $scope.discoveredDevices = DiscoveriesManager.getItems(); $scope.domains = DomainsManager.getItems(); $scope.machines = MachinesManager.getItems(); @@ -155,7 +155,7 @@ DiscoveriesManager, DomainsManager, MachinesManager, DevicesManager, SubnetsManager, VLANsManager, ConfigsManager]).then( function() { - $scope.loading = false; + $scope.loaded = true; $scope.networkDiscovery = ConfigsManager.getItemFromList( 'network_discovery'); }); diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/controllers/tests/test_dashboard.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/controllers/tests/test_dashboard.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/controllers/tests/test_dashboard.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/controllers/tests/test_dashboard.js 2016-12-21 07:56:19.000000000 -0500 @@ -76,7 +76,7 @@ it("sets initial $scope", function() { var controller = makeController(); - expect($scope.loading).toBe(true); + expect($scope.loaded).toBe(false); expect($scope.discoveredDevices).toBe(DiscoveriesManager.getItems()); expect($scope.domains).toBe(DomainsManager.getItems()); expect($scope.machines).toBe(MachinesManager.getItems()); diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/boot_images.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/boot_images.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/boot_images.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/boot_images.js 2016-12-21 07:56:19.000000000 -0500 @@ -93,7 +93,7 @@ // Return the overall title icon. $scope.getTitleIcon = function() { if($scope.bootResources.resources.length === 0) { - return 'icon--error'; + return 'icon--success-grey'; } else { return 'icon--success'; } diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/error_overlay.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/error_overlay.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/error_overlay.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/error_overlay.js 2016-12-21 07:56:19.000000000 -0500 @@ -48,7 +48,7 @@ // continuously. if(!angular.isDefined(window.jasmine)) { var image = new Image(); - image.src = "static/assets/images/icons/error_colour_white.svg"; + image.src = "static/assets/images/icons/error.svg"; image = new Image(); image.src = "static/assets/images/icons/error.png"; } diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/maas_obj_form.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/maas_obj_form.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/maas_obj_form.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/maas_obj_form.js 2016-12-21 07:56:19.000000000 -0500 @@ -497,6 +497,10 @@ } else { labelElement.addClass("u-margin--right"); } + if(attrs.labelLeft === "true") { + labelElement.addClass('u-padding--left'); + labelElement.addClass('u-position--relative'); + } element.append(labelElement); // Add a label info icon with tooltip. @@ -506,10 +510,17 @@ infoElement.addClass('icon'); infoElement.addClass('icon--info'); infoElement.addClass('tooltip'); + infoElement.addClass('u-margin--left-tiny'); + if(attrs.labelLeft === "true") { + infoElement.addClass('icon--left'); + infoElement.removeClass('u-margin--left-tiny'); + } infoElement.attr('aria-label', attrs.labelInfo); labelElement.text(labelElement.text() + ' '); labelElement.append(infoElement); } + + } // Add the wrapper for the input. diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/tests/test_boot_images.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/tests/test_boot_images.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/tests/test_boot_images.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/tests/test_boot_images.js 2016-12-21 07:56:19.000000000 -0500 @@ -184,7 +184,7 @@ var directive = compileDirective(); var scope = directive.isolateScope(); BootResourcesManager._data.resources = []; - expect(scope.getTitleIcon()).toBe('icon--error'); + expect(scope.getTitleIcon()).toBe('icon--success-grey'); }); it("returns success when resources", function() { diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/directives/tests/test_maas_obj_form.js 2016-12-21 07:56:19.000000000 -0500 @@ -957,6 +957,35 @@ }); }); + describe("labelLeft", function() { + + var directive; + beforeEach(function() { + $scope.obj = {}; + $scope.manager = {}; + var html = [ + '', + '', + '', + '' + ].join(''); + directive = compileDirective(html); + }); + + it("icon add with tooltip added in label", function() { + var label = directive.find("label"); + var icon = label.find("i"); + expect(label.text()).toBe("key "); + expect(icon.hasClass("icon")).toBe(true); + expect(icon.hasClass("icon--info")).toBe(true); + expect(icon.hasClass("icon--left")).toBe(true); + expect(icon.hasClass("tooltip")).toBe(true); + expect(icon.attr('aria-label')).toBe("My Info"); + }); + }); + describe("inputClass", function() { var directive; diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/js/angular/maas.js maas-2.1.3+bzr5573/src/maasserver/static/js/angular/maas.js --- maas-2.1.1+bzr5544/src/maasserver/static/js/angular/maas.js 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/js/angular/maas.js 2016-12-21 07:56:19.000000000 -0500 @@ -8,7 +8,8 @@ * conflicts with Django templates. */ -angular.module('MAAS', ['ngRoute', 'ngCookies', 'ngTagsInput']).config( +angular.module('MAAS', + ['ngRoute', 'ngCookies', 'ngTagsInput', 'sticky']).config( function($interpolateProvider, $routeProvider, $httpProvider) { $interpolateProvider.startSymbol('{$'); $interpolateProvider.endSymbol('$}'); diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/partials/boot-images.html maas-2.1.3+bzr5573/src/maasserver/static/partials/boot-images.html --- maas-2.1.1+bzr5544/src/maasserver/static/partials/boot-images.html 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/partials/boot-images.html 2016-12-21 07:56:19.000000000 -0500 @@ -2,7 +2,7 @@

- Ubuntu + Ubuntu

diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/partials/dashboard.html maas-2.1.3+bzr5573/src/maasserver/static/partials/dashboard.html --- maas-2.1.1+bzr5544/src/maasserver/static/partials/dashboard.html 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/partials/dashboard.html 2016-12-21 07:56:19.000000000 -0500 @@ -1,195 +1,205 @@ -

-
-

Getting started

-
    -
  • - Nodes: add, configure, deploy and commission machines. Manage nodes -
  • -
  • - Images: edit your preferences and import alternatives. Edit images -
  • -
  • - Networking: configure and edit fabrics and spaces, subnets and vlans. Configure networking -
  • -
  • Review the list below to add devices to MAAS.
  • -
-
-
-
-
-
-

{$ discoveredDevices.length $} items discovered

+
+ +
+
+
+ + + + + + + + + + + + + + + +
NameMac addressIPRackLast seen
+ No new discoveries +
+
+
+
+
+
+ +
+
+
    +
  • List of devices will not update as discovery is turned off
  • +
+
+
+
+
+
Name
+
Mac address
+
IP
+
Rack
+
Last seen
-
- Clear -
-
-
-
- No new discoveries... -
-
-
-
-
- {$ getDiscoveryName(discovery) $} - -
-
- {$ discovery.mac_address $} -
-
- {$ discovery.mac_organization || 'Unknown' $} -
-
- {$ discovery.ip $} -
-
- {$ discovery.observer_hostname $} -
-
- + +
+
+
+ {$ convertTo.hostname $} has been add to {$ site $}. + Go to the machine devices page. + Go to the device listing.
- Open + Clear
- -
- +
+
+ No new discoveries
-
-
- Close +
+
+
+
+ {$ getDiscoveryName(discovery) $} + +
+
+ {$ discovery.mac_address $}
+ {$ discovery.mac_organization || 'Unknown' $} +
+
+ {$ discovery.ip $} +
+
+ {$ discovery.observer_hostname $} +
+
+ +
+
+ Open +
-
-
-
-
-
-
Mac
-
{$ discovery.mac_address $}
-
IP
-
{$ discovery.ip $}
-
Rack
-
{$ discovery.observer_hostname $}
-
Last seen
-
-
-
-
-
-
Fabric
-
{$ discovery.fabric_name $}
-
VLAN
-
{$ getVLANName(discovery.vlan) $}
-
Subnet
-
{$ getSubnetName(discovery.subnet) $}
-
-
-
+ +
+
-
-
-
-
- -
- -
+
+
+ Close +
+
+
+
+
+
+
Mac
+
{$ discovery.mac_address $}
+
IP
+
{$ discovery.ip $}
+
Rack
+
{$ discovery.observer_hostname $}
+
Last seen
+
+
+
+
+
+
Fabric
+
{$ discovery.fabric_name $}
+
VLAN
+
{$ getVLANName(discovery.vlan) $}
+
Subnet
+
{$ getSubnetName(discovery.subnet) $}
+
- - -
-
- - -
+
-
-
-
- +
+
+
+
+ +
+ +
+
+ + +
+
+ + +
+
-
- Cancel - - - +
+
+ +
+
+ Cancel + + + +
-
- + +
-
-
+
+
diff -Nru maas-2.1.1+bzr5544/src/maasserver/static/partials/domain-details.html maas-2.1.3+bzr5573/src/maasserver/static/partials/domain-details.html --- maas-2.1.1+bzr5544/src/maasserver/static/partials/domain-details.html 2016-11-08 14:56:20.000000000 -0500 +++ maas-2.1.3+bzr5573/src/maasserver/static/partials/domain-details.html 2016-12-21 07:56:19.000000000 -0500 @@ -1,4 +1,4 @@ -