|
22 | 22 | import sys |
23 | 23 |
|
24 | 24 | from cinderclient import api_versions |
| 25 | +from openstack import exceptions as sdk_exceptions |
25 | 26 | from openstack.image import image_signer |
26 | 27 | from osc_lib.api import utils as api_utils |
27 | 28 | from osc_lib.cli import format_columns |
@@ -1561,3 +1562,245 @@ def take_action(self, parsed_args): |
1561 | 1562 | kwargs['data'] = fp |
1562 | 1563 |
|
1563 | 1564 | image_client.stage_image(image, **kwargs) |
| 1565 | + |
| 1566 | + |
| 1567 | +class ImportImage(command.ShowOne): |
| 1568 | + _description = _( |
| 1569 | + "Initiate the image import process.\n" |
| 1570 | + "This requires support for the interoperable image import process, " |
| 1571 | + "which was first introduced in Image API version 2.6 " |
| 1572 | + "(Glance 16.0.0 (Queens))" |
| 1573 | + ) |
| 1574 | + |
| 1575 | + def get_parser(self, prog_name): |
| 1576 | + parser = super().get_parser(prog_name) |
| 1577 | + |
| 1578 | + parser.add_argument( |
| 1579 | + 'image', |
| 1580 | + metavar='<image>', |
| 1581 | + help=_('Image to initiate import process for (name or ID)'), |
| 1582 | + ) |
| 1583 | + # TODO(stephenfin): Uncomment help text when we have this command |
| 1584 | + # implemented |
| 1585 | + parser.add_argument( |
| 1586 | + '--method', |
| 1587 | + metavar='<method>', |
| 1588 | + default='glance-direct', |
| 1589 | + dest='import_method', |
| 1590 | + choices=[ |
| 1591 | + 'glance-direct', |
| 1592 | + 'web-download', |
| 1593 | + 'glance-download', |
| 1594 | + 'copy-image', |
| 1595 | + ], |
| 1596 | + help=_( |
| 1597 | + "Import method used for image import process. " |
| 1598 | + "Not all deployments will support all methods. " |
| 1599 | + # "Valid values can be retrieved with the 'image import " |
| 1600 | + # "methods' command. " |
| 1601 | + "The 'glance-direct' method (default) requires images be " |
| 1602 | + "first staged using the 'image-stage' command." |
| 1603 | + ), |
| 1604 | + ) |
| 1605 | + parser.add_argument( |
| 1606 | + '--uri', |
| 1607 | + metavar='<uri>', |
| 1608 | + help=_( |
| 1609 | + "URI to download the external image " |
| 1610 | + "(only valid with the 'web-download' import method)" |
| 1611 | + ), |
| 1612 | + ) |
| 1613 | + parser.add_argument( |
| 1614 | + '--remote-image', |
| 1615 | + metavar='<REMOTE_IMAGE>', |
| 1616 | + help=_( |
| 1617 | + "The image of remote glance (ID only) to be imported " |
| 1618 | + "(only valid with the 'glance-download' import method)" |
| 1619 | + ), |
| 1620 | + ) |
| 1621 | + parser.add_argument( |
| 1622 | + '--remote-region', |
| 1623 | + metavar='<REMOTE_GLANCE_REGION>', |
| 1624 | + help=_( |
| 1625 | + "The remote Glance region to download the image from " |
| 1626 | + "(only valid with the 'glance-download' import method)" |
| 1627 | + ), |
| 1628 | + ) |
| 1629 | + parser.add_argument( |
| 1630 | + '--remote-service-interface', |
| 1631 | + metavar='<REMOTE_SERVICE_INTERFACE>', |
| 1632 | + help=_( |
| 1633 | + "The remote Glance service interface to use when importing " |
| 1634 | + "images " |
| 1635 | + "(only valid with the 'glance-download' import method)" |
| 1636 | + ), |
| 1637 | + ) |
| 1638 | + stores_group = parser.add_mutually_exclusive_group() |
| 1639 | + stores_group.add_argument( |
| 1640 | + '--store', |
| 1641 | + metavar='<STORE>', |
| 1642 | + dest='stores', |
| 1643 | + nargs='*', |
| 1644 | + help=_( |
| 1645 | + "Backend store to upload image to " |
| 1646 | + "(specify multiple times to upload to multiple stores) " |
| 1647 | + "(either '--store' or '--all-stores' required with the " |
| 1648 | + "'copy-image' import method)" |
| 1649 | + ), |
| 1650 | + ) |
| 1651 | + stores_group.add_argument( |
| 1652 | + '--all-stores', |
| 1653 | + help=_( |
| 1654 | + "Make image available to all stores " |
| 1655 | + "(either '--store' or '--all-stores' required with the " |
| 1656 | + "'copy-image' import method)" |
| 1657 | + ), |
| 1658 | + ) |
| 1659 | + parser.add_argument( |
| 1660 | + '--allow-failure', |
| 1661 | + action='store_true', |
| 1662 | + dest='allow_failure', |
| 1663 | + default=True, |
| 1664 | + help=_( |
| 1665 | + 'When uploading to multiple stores, indicate that the import ' |
| 1666 | + 'should be continue should any of the uploads fail. ' |
| 1667 | + 'Only usable with --stores or --all-stores' |
| 1668 | + ), |
| 1669 | + ) |
| 1670 | + parser.add_argument( |
| 1671 | + '--disallow-failure', |
| 1672 | + action='store_true', |
| 1673 | + dest='allow_failure', |
| 1674 | + default=True, |
| 1675 | + help=_( |
| 1676 | + 'When uploading to multiple stores, indicate that the import ' |
| 1677 | + 'should be reverted should any of the uploads fail. ' |
| 1678 | + 'Only usable with --stores or --all-stores' |
| 1679 | + ), |
| 1680 | + ) |
| 1681 | + parser.add_argument( |
| 1682 | + '--wait', |
| 1683 | + action='store_true', |
| 1684 | + help=_('Wait for operation to complete'), |
| 1685 | + ) |
| 1686 | + return parser |
| 1687 | + |
| 1688 | + def take_action(self, parsed_args): |
| 1689 | + image_client = self.app.client_manager.image |
| 1690 | + |
| 1691 | + try: |
| 1692 | + import_info = image_client.get_import_info() |
| 1693 | + except sdk_exceptions.ResourceNotFound: |
| 1694 | + msg = _( |
| 1695 | + 'The Image Import feature is not supported by this deployment' |
| 1696 | + ) |
| 1697 | + raise exceptions.CommandError(msg) |
| 1698 | + |
| 1699 | + import_methods = import_info.import_methods['value'] |
| 1700 | + |
| 1701 | + if parsed_args.import_method not in import_methods: |
| 1702 | + msg = _( |
| 1703 | + "The '%s' import method is not supported by this deployment. " |
| 1704 | + "Supported: %s" |
| 1705 | + ) |
| 1706 | + raise exceptions.CommandError( |
| 1707 | + msg % (parsed_args.import_method, ', '.join(import_methods)), |
| 1708 | + ) |
| 1709 | + |
| 1710 | + if parsed_args.import_method == 'web-download': |
| 1711 | + if not parsed_args.uri: |
| 1712 | + msg = _( |
| 1713 | + "The '--uri' option is required when using " |
| 1714 | + "'--method=web-download'" |
| 1715 | + ) |
| 1716 | + raise exceptions.CommandError(msg) |
| 1717 | + else: |
| 1718 | + if parsed_args.uri: |
| 1719 | + msg = _( |
| 1720 | + "The '--uri' option is only supported when using " |
| 1721 | + "'--method=web-download'" |
| 1722 | + ) |
| 1723 | + raise exceptions.CommandError(msg) |
| 1724 | + |
| 1725 | + if parsed_args.import_method == 'glance-download': |
| 1726 | + if not (parsed_args.remote_region and parsed_args.remote_image): |
| 1727 | + msg = _( |
| 1728 | + "The '--remote-region' and '--remote-image' options are " |
| 1729 | + "required when using '--method=web-download'" |
| 1730 | + ) |
| 1731 | + raise exceptions.CommandError(msg) |
| 1732 | + else: |
| 1733 | + if parsed_args.remote_region: |
| 1734 | + msg = _( |
| 1735 | + "The '--remote-region' option is only supported when " |
| 1736 | + "using '--method=glance-download'" |
| 1737 | + ) |
| 1738 | + raise exceptions.CommandError(msg) |
| 1739 | + |
| 1740 | + if parsed_args.remote_image: |
| 1741 | + msg = _( |
| 1742 | + "The '--remote-image' option is only supported when using " |
| 1743 | + "'--method=glance-download'" |
| 1744 | + ) |
| 1745 | + raise exceptions.CommandError(msg) |
| 1746 | + |
| 1747 | + if parsed_args.remote_service_interface: |
| 1748 | + msg = _( |
| 1749 | + "The '--remote-service-interface' option is only " |
| 1750 | + "supported when using '--method=glance-download'" |
| 1751 | + ) |
| 1752 | + raise exceptions.CommandError(msg) |
| 1753 | + |
| 1754 | + if parsed_args.import_method == 'copy-image': |
| 1755 | + if not (parsed_args.stores or parsed_args.all_stores): |
| 1756 | + msg = _( |
| 1757 | + "The '--stores' or '--all-stores' options are required " |
| 1758 | + "when using '--method=copy-image'" |
| 1759 | + ) |
| 1760 | + raise exceptions.CommandError(msg) |
| 1761 | + |
| 1762 | + image = image_client.find_image(parsed_args.image) |
| 1763 | + |
| 1764 | + if not image.container_format and not image.disk_format: |
| 1765 | + msg = _( |
| 1766 | + "The 'container_format' and 'disk_format' properties " |
| 1767 | + "must be set on an image before it can be imported" |
| 1768 | + ) |
| 1769 | + raise exceptions.CommandError(msg) |
| 1770 | + |
| 1771 | + if parsed_args.import_method == 'glance-direct': |
| 1772 | + if image.status != 'uploading': |
| 1773 | + msg = _( |
| 1774 | + "The 'glance-direct' import method can only be used with " |
| 1775 | + "an image in status 'uploading'" |
| 1776 | + ) |
| 1777 | + raise exceptions.CommandError(msg) |
| 1778 | + elif parsed_args.import_method == 'web-download': |
| 1779 | + if image.status != 'queued': |
| 1780 | + msg = _( |
| 1781 | + "The 'web-download' import method can only be used with " |
| 1782 | + "an image in status 'queued'" |
| 1783 | + ) |
| 1784 | + raise exceptions.CommandError(msg) |
| 1785 | + elif parsed_args.import_method == 'copy-image': |
| 1786 | + if image.status != 'active': |
| 1787 | + msg = _( |
| 1788 | + "The 'copy-image' import method can only be used with " |
| 1789 | + "an image in status 'active'" |
| 1790 | + ) |
| 1791 | + raise exceptions.CommandError(msg) |
| 1792 | + |
| 1793 | + image_client.import_image( |
| 1794 | + image, |
| 1795 | + method=parsed_args.import_method, |
| 1796 | + # uri=parsed_args.uri, |
| 1797 | + # remote_region=parsed_args.remote_region, |
| 1798 | + # remote_image=parsed_args.remote_image, |
| 1799 | + # remote_service_interface=parsed_args.remote_service_interface, |
| 1800 | + stores=parsed_args.stores, |
| 1801 | + all_stores=parsed_args.all_stores, |
| 1802 | + all_stores_must_succeed=not parsed_args.allow_failure, |
| 1803 | + ) |
| 1804 | + |
| 1805 | + info = _format_image(image) |
| 1806 | + return zip(*sorted(info.items())) |
0 commit comments